Skip to content

Commit f92be51

Browse files
committed
feat: add small improvements in code and documentation
1 parent 3eeba11 commit f92be51

33 files changed

Lines changed: 229 additions & 152 deletions

.flake8

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
max-line-length = 88
33
ignore =
44
W503,
5+
E203
56
ANN002, ANN003, ANN101, ANN102, ANN204, ANN401,
6-
F405,
77
A003,
8+
S113, S311,
89

910
exclude =
1011
.git

.github/workflows/ci.yml

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
11
name: CI
22

33
on:
4-
push:
5-
branches: [main]
64
pull_request:
7-
branches: [main]
5+
branches: [ master ]
6+
push:
7+
branches: [ master ]
88

99
jobs:
1010
lint-test:
1111
runs-on: ubuntu-latest
12-
strategy:
13-
matrix:
14-
python-version: ["3.10"]
15-
1612
steps:
1713
# Загрузка кода
1814
- uses: actions/checkout@v4
1915

2016
# Установка Python
2117
- uses: actions/setup-python@v5
2218
with:
23-
python-version: ${{ matrix.python-version }}
19+
python-version: "3.10"
2420

2521
# Кеширование зависимостей
2622
- uses: actions/cache@v3
@@ -30,11 +26,9 @@ jobs:
3026

3127
# Установка зависимостей
3228
- name: Install dependencies
33-
run: |
34-
python -m pip install --upgrade pip
35-
pip install -r requirements.txt -r requirements-dev.txt
29+
run: pip install -r requirements.txt -r requirements-dev.txt
3630

37-
# Запуск линтеров
31+
# Запуск форматеров и линтеров
3832
- name: Run Black
3933
run: black src/ --check
4034

@@ -43,3 +37,10 @@ jobs:
4337

4438
- name: Run flake8
4539
run: flake8 src/
40+
41+
- name: Run pyupgrade
42+
run: |
43+
pyupgrade --py38-plus \
44+
$(find . -name '*.py' \
45+
-not -path './.git/*' \
46+
-not -path './solutions/*/solution.py')

.pre-commit-config.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,12 @@ repos:
3030
flake8-comprehensions,
3131
flake8-annotations,
3232
flake8-builtins,
33-
flake8-json,
33+
flake8-bandit,
3434
]
35+
36+
- repo: https://github.com/asottile/pyupgrade
37+
rev: v3.20.0
38+
hooks:
39+
- id: pyupgrade # Автоматическое обновление синтаксиса
40+
args: [--py310-plus]
41+
exclude: ^solutions/.*/solution\.py$

README.md

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
<!-- language: ru -->
22
# Code tester
33

4-
[![Status: Active](https://img.shields.io/badge/status-active-brightgreen)]()
5-
[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
4+
[![Status: Active](https://img.shields.io/badge/status-active-brightgreen)](https://github.com/vladisnut/codetester)
65
[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
6+
[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
77
[![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
88
[![Imports](https://img.shields.io/badge/imports-isort-%231674b1)](https://pycqa.github.io/isort/)
99
[![Linter](https://img.shields.io/badge/linter-flake8-%23007C9B)](https://flake8.pycqa.org/)
1010

1111
CLI-проект для тестирования кода алгоритмических задач на Python.
1212
Поддерживает несколько видов тестирования (в зависимости от формата задачи)
13-
и загрузку задач с некоторых онлайн-ресурсов
14-
(на данный момент доступны `leetcode` и `codeforces`).
13+
и загрузку задач с некоторых онлайн-ресурсов.
1514

1615
## Оглавление
1716

17+
- [Возможности](#возможности)
1818
- [Установка](#установка)
1919
- [Пример использования](#пример-использования)
2020
- [Команды](#Команды)
21+
- [Создать решение](#создать-решение)
22+
- [Загрузить задачу](#загрузить-задачу)
23+
- [Тестировать решение](#тестировать-решение)
2124
- [Структура проекта](#структура-проекта)
2225
- [Решение](#решение)
2326
- [Структура файлов](#структура-файлов)
@@ -31,6 +34,13 @@ CLI-проект для тестирования кода алгоритмиче
3134
- [stream](#stream)
3235
- [Примеры решений](#примеры-решений)
3336

37+
## Возможности
38+
39+
- Загрузка задач с `LeetCode` и `Codeforces`.
40+
- Тестирование как единицы (функции, класса), так и через поток ввода-вывода.
41+
- Конфигурация тестирования (можно настроить генерацию тестовых случаев, свою валидацию теста, свой скрипт выполнения теста).
42+
- Удобный ввод тестовых случаев через текстовый файл.
43+
3444
## Установка
3545

3646
1. Клонируйте репозиторий:
@@ -71,42 +81,48 @@ CLI-проект для тестирования кода алгоритмиче
7181

7282
## Команды
7383

74-
**1. Создать решение.** Создает папку в `solutions` с базовой структурой файлов [решения](#решение).
84+
### Создать решение
85+
86+
Создает папку в `solutions` с базовой структурой файлов [решения](#решение).
7587

7688
```
77-
create [solution_name] [solution_name]
89+
create [solution] [--template|-t <name>]
7890
```
7991

80-
| Параметр | Обязательный | Описание | Допустимые значения |
81-
|----------------:|--------------|----------------------------------------|--------------------------------------------------------------------------------------------------------------------|
82-
| `solution_name` | Нет | Имя решения | Допустимое имя папки, по умолчанию: значение параметра конфигурации `SOLUTION_DEFAULT_NAME` |
83-
| `template_name` | Нет | Имя шаблона скрипта для решения задачи | Имя файла из папки `assets/templates/solution`, по умолчанию: значение параметра конфигурации `MAIN_FUNCTION_NAME` |
92+
| Параметр | Тип | Обязательный | Описание | Допустимые значения |
93+
|------------|-------------|--------------|----------------------------------------|--------------------------------------------------------------------------------------------------------------------|
94+
| `solution` | Позиционный | Нет | Имя решения | Допустимое имя папки, по умолчанию: значение параметра конфигурации `SOLUTION_DEFAULT_NAME` |
95+
| `template` | Именованный | Нет | Имя шаблона скрипта для решения задачи | Имя файла из папки `assets/templates/solution`, по умолчанию: значение параметра конфигурации `MAIN_FUNCTION_NAME` |
96+
97+
### Загрузить задачу
8498

85-
**2. Загрузить задачу.** Загружает данные задачи с внешнего ресурса и создает для нее решение.
99+
Загружает данные задачи с внешнего ресурса и создает для нее решение.
86100
Если параметр конфигурации `CREATE_NEW_SOLUTIONS = True`, то будет создано новое решение
87101
с slug'ом задачи, иначе с именем `main`.
88102
89103
```
90-
load <slug> [--source leetcode|codeforces] [--open]
104+
load <slug> [--source|-s <leetcode|codeforces>] [--open|-o]
91105
```
92106
93-
| Параметр | Обязательный | Описание | Допустимые значения |
94-
|-----------:|--------------|---------------------------|--------------------------------------------------------------------------------------------------------------|
95-
| `slug` | Да | Slug, ID или URL задачи | URL задачи, если `--source` не задан, иначе slug, ID, URL задачи или `!daily` для ежедневной задачи LeetCode |
96-
| `--source` | Нет | Имя ресурса с задачей | `leetcode`, `codeforces` |
97-
| `--open` | Нет | Открыть задачу в браузере | Флаг (не требует значения) |
107+
| Параметр | Тип | Обязательный | Описание | Допустимые значения |
108+
|----------|-------------|--------------|---------------------------|------------------------------------------------------------------------------------------------------------|
109+
| `slug` | Позиционный | Да | Slug, ID или URL задачи | URL задачи, если `source` не задан, иначе slug, ID, URL задачи или `!daily` для ежедневной задачи LeetCode |
110+
| `source` | Именованный | Нет | Имя ресурса с задачей | `leetcode`, `codeforces` |
111+
| `open` | Именованный | Нет | Открыть задачу в браузере | Флаг (не требует значения) |
112+
113+
### Тестировать решение
98114
99-
**3. Протестировать решение.** Запускает тесты решения.
115+
Запускает тесты решения.
100116
101117
```
102-
test [solution] [--time] [--debug]
118+
test [solution] [--time|-t] [--debug|-d]
103119
```
104120
105-
| Параметр | Обязательный | Описание | Допустимые значения |
106-
|-----------:|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|
107-
| `solution` | Нет | Имя решения. Если значение не задано и параметр конфигурации `LAUNCH_LAST_MODIFIED_SOLUTIONS = True`, то будет запущено последнее измененное решение | Имя существующего решения (имя одной из папок в `solutions`) |
108-
| `--time` | Нет | Показывать время выполнения каждого теста | Флаг (не требует значения) |
109-
| `--debug` | Нет | Режим отладки, при котором тестировщики могут выводить дополнительную информацию | Флаг (не требует значения) |
121+
| Параметр | Тип | Обязательный | Описание | Допустимые значения |
122+
|-----------:|-------------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|
123+
| `solution` | Позиционный | Нет | Имя решения. Если значение не задано и параметр конфигурации `LAUNCH_LAST_MODIFIED_SOLUTION = True`, то будет запущено последнее измененное решение | Имя существующего решения (имя одной из папок в `solutions`) |
124+
| `time` | Именованный | Нет | Показывать время выполнения каждого теста | Флаг (не требует значения) |
125+
| `debug` | Именованный | Нет | Режим отладки, при котором тестировщики могут выводить дополнительную информацию | Флаг (не требует значения) |
110126
111127
## Структура проекта
112128
@@ -179,7 +195,7 @@ test [solution] [--time] [--debug]
179195
Чтобы он был активирован, его нужно присвоить переменной `RUNNER`.
180196
181197
```python
182-
def runner(target: Type | Callable, args: Sequence) -> Any:
198+
def runner(target: type | Callable, args: Sequence) -> Any:
183199
"""
184200
Пользовательская настройка запуска тестируемой цели.
185201
@@ -197,7 +213,7 @@ RUNNER = runner
197213
<summary><b>Пример</b></summary>
198214
199215
```python
200-
def runner(target: Type | Callable, args: Sequence) -> Any:
216+
def runner(target: type | Callable, args: Sequence) -> Any:
201217
"""
202218
Сериализация и десериализация аргумента.
203219
"""

assets/templates/settings.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
from typing import Any, Callable, Sequence, Type
1+
from collections.abc import Callable, Sequence
2+
from typing import Any
23

34

4-
def runner(target: Type | Callable, args: Sequence) -> Any:
5+
def runner(target: type | Callable, args: Sequence) -> Any:
56
"""
67
Пользовательская настройка запуска тестируемой цели.
78

requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
black==25.1.0
22
flake8==7.2.0
33
isort==6.0.1
4+
pyupgrade==3.20.0

solutions/example/settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import random
2-
from typing import Any, Sequence
2+
from collections.abc import Sequence
3+
from typing import Any
34

45

56
def validator(

src/api/codeforces_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from selenium.webdriver.chrome.service import Service
77
from webdriver_manager.chrome import ChromeDriverManager
88

9-
from config import DEFAULT_SOLUTION_TEMPLATE_NAME
9+
from src.config import DEFAULT_SOLUTION_TEMPLATE_NAME
1010
from src.problem import Problem
1111
from src.testing.utils import get_template
1212

src/api/leetcode_api.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import json
22
import re
33
import typing
4-
from typing import Optional
54

65
import requests
76

8-
from src.nodes.node import get_node_classes
7+
from src.nodes.node import Node, get_nodes
98
from src.problem import Problem
109
from src.utils.general import string_to_json, to_json_string
1110

@@ -31,7 +30,7 @@ def parse_leetcode_problem(obj: dict) -> Problem:
3130
)
3231

3332

34-
def get_leetcode_problem_slug(problem_id: int) -> Optional[str]:
33+
def get_leetcode_problem_slug(problem_id: int) -> str | None:
3534
url = LEETCODE_GRAPHQL_URL
3635
headers = {
3736
"Content-Type": "application/json",
@@ -165,9 +164,12 @@ def proc_leetcode_code_snippet(text: str) -> str:
165164
typing_names = [
166165
name for name in dir(typing) if not name.startswith("_") and name.istitle()
167166
]
168-
node_classes = [x.ALT_NAME for x in get_node_classes() if x.ALT_NAME != "Node"]
167+
node_classes_replace = [x.ALT_NAME for x in get_nodes() if x.ALT_NAME != "Node"]
168+
node_classes_import = {f"'{x.ALT_NAME}'": x for x in get_nodes()}
169+
170+
commented_imports: set[type[Node]] = set()
171+
import_types: set[str] = set()
169172
lines = text.splitlines()
170-
types = set()
171173
i = 0
172174

173175
while i < len(lines):
@@ -194,15 +196,21 @@ def proc_leetcode_code_snippet(text: str) -> str:
194196
if words and words[0] == "def":
195197
# Заменить классы Node на имена в кавычках (чтобы не импортировать).
196198
start_args = lines[i].find("(") + 1
197-
for node_class in node_classes:
199+
for name in node_classes_replace:
198200
lines[i] = lines[i][:start_args] + lines[i][start_args:].replace(
199-
node_class, f"'{node_class}'"
201+
name, f"'{name}'"
200202
)
201203

204+
# Добавить закомментированные импорты Nodes
205+
# (на случай если их нужно будет импортировать).
206+
for name, node_class in node_classes_import.items():
207+
if name in lines[i][start_args:]:
208+
commented_imports.add(node_class)
209+
202210
# Добавить импорты типов.
203-
types.update(
204-
t for t in typing_names if t in re.findall(r"\w+", lines[i])
205-
)
211+
for name in typing_names:
212+
if name in re.findall(r"\w+", lines[i]):
213+
import_types.add(name)
206214

207215
# Добавить pass в пустые реализации.
208216
while i < len(lines) and lines[i].strip():
@@ -213,8 +221,24 @@ def proc_leetcode_code_snippet(text: str) -> str:
213221

214222
i += 1
215223

224+
if import_types or commented_imports:
225+
lines.insert(0, "")
226+
227+
if commented_imports:
228+
lines.insert(0, "")
229+
for node_class in commented_imports:
230+
lines.insert(
231+
0,
232+
(
233+
f"# from {node_class.__module__} import "
234+
f"{node_class.__name__} as {node_class.ALT_NAME}"
235+
),
236+
)
237+
238+
if import_types:
239+
lines.insert(0, f'from typing import {", ".join(import_types)}\n')
240+
216241
if lines[-1].strip():
217242
lines.append("")
218243

219-
imports = f'from typing import {", ".join(types)}\n\n\n' if types else ""
220-
return imports + "\n".join(lines)
244+
return "\n".join(lines)

0 commit comments

Comments
 (0)