Skip to content
Open
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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.venv/
__pycache__/
.coverage
.pytest_cache/
.vscode
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❗ папку с настройками проекта .vscode не нужно игнорить - как раз удобно шарить между командой общий сетап проекта. При этом пользовательские же настройки хранятся в другом месте.

.ruff_cache
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.4
hooks:
- id: ruff
args: [ --fix ]
- id: ruff-format
3 changes: 2 additions & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[pytest]
addopts = --cov=. --cov-branch --testdox -vv
; addopts = --cov=. --cov-branch --testdox --cov-fail-under=60 -vv
addopts = --testdox -vv
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 лучше вернуть покрытие и если нужно, то по требованию его можно отключить при запуске команды: pytest --no-cov

13 changes: 12 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
asarPy==1.0.1
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 давай как нибудь попробуем использовать pip-tools, или более современный его аналог - uv. Хотя начать лучше с кого-либо из poetry, pdm, pipenv.

Нам нужно разделять основные зависимости проекта от прочих зависимостей времени разработки (dev, lint, test, docs), а также от различать их от дочерних зависимостей.

При этом нам все еще нужен общий лок файл зависимостей с конкретными версиями для воспроизводимости окружения (независимости его от времени установки)

attrs==23.2.0
cfgv==3.4.0
coverage==7.4.3
distlib==0.3.8
exceptiongroup==1.2.0
Faker==24.2.0
Faker==24.4.0
filelock==3.13.3
identify==2.5.35
iniconfig==2.0.0
markdown-it-py==3.0.0
mdurl==0.1.2
nodeenv==1.8.0
packaging==23.2
platformdirs==4.2.0
pluggy==1.4.0
pprintpp==0.4.0
pre-commit==3.7.0
Pygments==2.17.2
pytest==7.2.1
pytest-clarity==1.0.1
pytest-cov==4.1.0
pytest-testdox==3.1.0
python-dateutil==2.9.0.post0
PyYAML==6.0.1
rich==13.7.1
ruff==0.3.4
six==1.16.0
tomli==2.0.1
virtualenv==20.25.1
76 changes: 75 additions & 1 deletion ruff.toml
Original file line number Diff line number Diff line change
@@ -1 +1,75 @@
line-length = 100
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
"functions"
]

# Same as Black.
line-length = 99
indent-width = 4

[lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F"]
ignore = []

# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[format]
# Like Black, use double quotes for strings.
quote-style = "double"

# Like Black, indent with spaces, rather than tabs.
indent-style = "space"

# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false

# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"

# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false

# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"
4 changes: 1 addition & 3 deletions tests/level_1/test_four_bank_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ def test__parse_ineco_expense__return_correct_amount(sms, amount, make_sms, card
("200 200.5, 0123 13.03.24 16:36 12:01 shop", "2024-03-13T16:36:00"),
],
)
def test__parse_ineco_expense__return_correct_spent_at(
sms, expected_spent_at, make_sms, cards
):
def test__parse_ineco_expense__return_correct_spent_at(sms, expected_spent_at, make_sms, cards):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 хороший пример почему автоформат на прекоммит хуке не здорово - эти изменения казалось бы не должны были попасить в этот пулл-риквест - но попали... Лучше все-таки оставлять линтер для подсвечивания по ошибкам, но не включать автофикс

sms_message = make_sms(text=sms)
expense = parse_ineco_expense(sms_message, cards)
assert expense.spent_at.isoformat() == expected_spent_at
4 changes: 1 addition & 3 deletions tests/level_1/test_two_date_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ def test__compose_datetime_from__any_time_tomorrow_returns_tomorrow(time_str, to
assert compose_datetime_from("tomorrow", time_str).date() == tomorrow


@pytest.mark.parametrize(
"time_str, expected", [("12:00", 12), ("00:00", 0), ("23:59", 23)]
)
@pytest.mark.parametrize("time_str, expected", [("12:00", 12), ("00:00", 0), ("23:59", 23)])
def test__compose_datetime_from__hour_parsing_valid(time_str, expected):
assert compose_datetime_from("tomorrow", time_str).hour == expected

Expand Down
12 changes: 3 additions & 9 deletions tests/level_2/test_five_replace_word.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@


def test__replace_word__replace_from_in_text_return_valid_text():
assert (
replace_word("some words to replace", "some", "any") == "any words to replace"
)
assert replace_word("some words to replace", "some", "any") == "any words to replace"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 а вот за это спасибо - этот вариант со скобками был крайне ужасным



def test__replace_word__replace_from_not_in_text_return_old_text():
assert (
replace_word("some words to replace", "bad", "any") == "some words to replace"
)
assert replace_word("some words to replace", "bad", "any") == "some words to replace"


@pytest.mark.parametrize(
Expand All @@ -23,7 +19,5 @@ def test__replace_word__replace_from_not_in_text_return_old_text():
("SOMe words to replace", "some", "any", "any words to replace"),
],
)
def test__replace_word__ignore_case_return_valid_text(
text, replace_from, replace_to, expected
):
def test__replace_word__ignore_case_return_valid_text(text, replace_from, replace_to, expected):
assert replace_word(text, replace_from, replace_to) == expected
4 changes: 1 addition & 3 deletions tests/level_2/test_four_sentiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ def test__check_tweet_sentiment__bad_and_good_words_are_equal_return_None(
"You're not good enough",
],
)
def test__check_tweet_sentiment__more_good_words_return_GOOD(
text, good_words, bad_words
):
def test__check_tweet_sentiment__more_good_words_return_GOOD(text, good_words, bad_words):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 не используй заглавные буквы в названии функций

assert check_tweet_sentiment(text, good_words, bad_words) == "GOOD"


Expand Down
22 changes: 8 additions & 14 deletions tests/level_2/test_two_square_equation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,11 @@
@pytest.mark.parametrize(
"a, b, c",
[
(
90.2,
8.3,
1.4,
),
(
55.5,
0.0,
2.2,
),
(90.2, 8.3, 1.4),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 отличная, такая компактная запись - гораздо приятнее

(55.5, 0.0, 2.2),
],
)
def test__solve_square_equation__discriminant_lt_zero_return_no_roots(a, b, c):
def test__solve_square_equation__return_no_roots_if_discriminant_lt_zero(a, b, c):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 здорово поправил название

assert solve_square_equation(a, b, c) == (None, None)


Expand All @@ -29,15 +21,17 @@ def test__solve_square_equation__discriminant_lt_zero_return_no_roots(a, b, c):
(2.5, 1.0, (-0.4, None)),
],
)
def test__solve_square_equation__square_coef_zero_linear_coef_not_zero_return_one_root(b, c, root):
def test__solve_square_equation__return_one_root_with_zeroes_in_square_and_linear_coefficients(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 в этом и следующем тестах названия отлично поправлены

b, c, root
):
assert solve_square_equation(0.0, b, c) == root


@pytest.mark.parametrize(
"c",
[1.0, -57.2, 0.0],
)
def test__solve_square_equation__square_coef_zero_linear_coef_zero_return_no_roots(c):
def test__solve_square_equation__return_no_roots__square_coef_zero_linear_coef_zero(c):
assert solve_square_equation(0.0, 0.0, c) == (None, None)


Expand All @@ -48,7 +42,7 @@ def test__solve_square_equation__square_coef_zero_linear_coef_zero_return_no_roo
(2.0, 4.0, 0.0, (-2.0, 0.0)),
],
)
def test__solve_square_equation__square_coef_not_zero_linear_coef_not_zero_return_two_numbers(
def test__solve_square_equation__return_two_numbers_if_square_coef_not_zero_linear_coef_not_zero(
a, b, c, roots
):
assert solve_square_equation(a, b, c) == roots
49 changes: 29 additions & 20 deletions tests/level_3/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,34 @@
import typing as t

import pytest
from faker import Faker

from functions.level_3.models import BankCard, Expense, ExpenseCategory

NOT_SET: t.Any = "____"

faker = Faker()

def defval(value: t.Any, default: t.Any) -> t.Any:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 удобная helper функция

return default if value is NOT_SET else value


@pytest.fixture
def make_card(faker):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 это не фикстура-фабрика, поэтому стоит убрать make из ее названия
💡 тоже самое касается трех фикстур ниже

return BankCard(last_digits=faker.credit_card_number()[:-4], owner=faker.name())


@pytest.fixture
def make_spent_at(faker):
return faker.past_date()


@pytest.fixture
def make_spent_in(faker):
return faker.company()


@pytest.fixture
def make_amount_lt_five_thousand(faker):
return faker.pyfloat(max_value=5000)


@pytest.fixture
Expand All @@ -22,24 +43,12 @@ def inner(
spent_at: datetime.datetime = NOT_SET,
category: ExpenseCategory = NOT_SET,
):
amount = (
faker.pyfloat(left_digits=3, right_digits=2, positive=True)
if amount is NOT_SET
else amount
)
currency = faker.currency_code() if currency is NOT_SET else currency
card = (
BankCard(last_digits=faker.credit_card_number()[-4:], owner=faker.name())
if card is NOT_SET
else card
)
spent_in = faker.company() if spent_in is NOT_SET else spent_in
spent_at = faker.date_time_this_year() if spent_at is NOT_SET else spent_at
category = (
faker.random_element(elements=ExpenseCategory)
if category is NOT_SET
else category
)
amount = defval(amount, faker.pyfloat(left_digits=3, right_digits=2, positive=True))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 красиво

currency = defval(currency, faker.currency_code())
card = defval(card, make_card)
spent_in = defval(spent_in, make_spent_in)
spent_at = defval(spent_at, faker.past_date())
category = defval(category, faker.enum(ExpenseCategory))

return Expense(
amount=decimal.Decimal(str(amount)),
Expand Down
Loading