Skip to content

Commit 82be47d

Browse files
committed
use uv for tests, run mypy
1 parent a425fe4 commit 82be47d

File tree

11 files changed

+651
-51
lines changed

11 files changed

+651
-51
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,14 @@ jobs:
77
runs-on: ubuntu-latest
88
strategy:
99
matrix:
10-
python-version: ["3.9", "3.13"]
10+
python-version: ["3.10", "3.13"]
1111
steps:
1212
- uses: actions/checkout@v4
1313
with:
1414
fetch-depth: 0
1515
- uses: actions/setup-python@v5
1616
with:
1717
python-version: ${{ matrix.python-version }}
18-
- name: Install Dependencies
19-
run: pip install tox wheel setuptools pre-commit
20-
- name: Run lint
21-
run: pre-commit run -a
22-
- name: Run tests
23-
run: tox -e py
18+
- uses: astral-sh/setup-uv@v5
19+
- run: make lint
20+
- run: make test

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.9.2
3+
rev: v0.11.11
44
hooks:
5-
- id: ruff
5+
- id: ruff-check
66
args: ["--fix"]
77
- id: ruff-format

Makefile

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1+
# Run tests and linters
2+
.PHONY: all
3+
all: lint test
4+
15
# Run tests
26
.PHONY: test
37
test:
4-
tox -e py
8+
uv run pytest
59

610
# Run linters
711
.PHONY: lint
812
lint:
9-
pre-commit run -a
13+
uv run pre-commit run -a
14+
uv run mypy
1015

1116
# Build the distribution (sdist and wheel).
1217
.PHONY: dist
@@ -16,6 +21,12 @@ dist:
1621
python -m build
1722
twine check dist/*
1823

24+
# Update the lock file.
25+
.PHONY: update
26+
update:
27+
uv lock --upgrade
28+
uv run pre-commit autoupdate
29+
1930
# Upload the distribution
2031
.PHONY: upload
2132
upload: dist

fava_plugins/split_income.py

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,31 +50,41 @@
5050
Income:Net:Bonus 100.00 EUR
5151
"""
5252

53+
from __future__ import annotations
54+
5355
import ast
5456
import collections
5557
import copy
5658
import re
59+
from decimal import Decimal
60+
from typing import Any
61+
from typing import TYPE_CHECKING
5762

5863
from beancount.core import convert
5964
from beancount.core import data
6065
from beancount.core import getters
6166
from beancount.core.inventory import Inventory
62-
from beancount.core.number import Decimal
63-
from beancount.core.number import ZERO
67+
68+
if TYPE_CHECKING:
69+
from beancount.core.data import Directive
6470

6571
__plugins__ = ("split_income",)
6672

6773
SplitIncomeError = collections.namedtuple(
68-
"SplitIncomeError", "source message entry"
74+
"SplitIncomeError",
75+
"source message entry",
6976
)
7077

78+
ZERO = Decimal()
79+
7180

72-
def split_income(entries, options_map, config_str):
81+
def split_income(
82+
entries: list[Directive], options_map: Any, config_str: str
83+
) -> tuple[list[Directive], list[SplitIncomeError]]:
7384
"""Split income transactions."""
74-
# pylint: disable=too-many-locals
7585

7686
errors = []
77-
new_entries = []
87+
new_entries: list[Directive] = []
7888
new_accounts = set()
7989
config = {
8090
"income": options_map["name_income"],
@@ -93,7 +103,7 @@ def split_income(entries, options_map, config_str):
93103
data.new_metadata(options_map["filename"], 0),
94104
f"Syntax error in config: {config_str}",
95105
None,
96-
)
106+
),
97107
)
98108
return entries, errors
99109

@@ -107,14 +117,15 @@ def split_income(entries, options_map, config_str):
107117
# The new entry containing the raw income and taxes.
108118
new_entry = copy.deepcopy(entry)
109119
new_entry = new_entry._replace(
110-
postings=[], tags=frozenset({config["tag"]} | entry.tags)
120+
postings=[],
121+
tags=frozenset({config["tag"]} | entry.tags),
111122
)
112123
new_entries.append(new_entry)
113124

114-
income = collections.defaultdict(Inventory)
115-
taxes = collections.defaultdict(Decimal)
125+
income: dict[str, Inventory] = collections.defaultdict(Inventory)
126+
taxes: dict[str, Decimal] = collections.defaultdict(Decimal)
116127
for posting in list(entry.postings):
117-
weight = convert.get_weight(posting)
128+
weight = convert.get_weight(posting) # type: ignore[no-untyped-call]
118129
if posting.account.startswith(config["income"]):
119130
new_entry.postings.append(posting)
120131
entry.postings.remove(posting)
@@ -126,7 +137,8 @@ def split_income(entries, options_map, config_str):
126137

127138
for account, inv in income.items():
128139
net_account = account.replace(
129-
config["income"], config["net_income"]
140+
config["income"],
141+
config["net_income"],
130142
)
131143
if net_account not in new_accounts:
132144
new_accounts.add(net_account)
@@ -135,19 +147,25 @@ def split_income(entries, options_map, config_str):
135147
data.new_metadata("<split_income>", 0),
136148
entry.date,
137149
net_account,
150+
[],
138151
None,
139-
None,
140-
)
152+
),
141153
)
142154

143155
for pos in inv:
144156
amount = pos.units
145157
number = amount.number + taxes.pop(amount.currency, ZERO)
146158
data.create_simple_posting(
147-
entry, net_account, number, amount.currency
159+
entry,
160+
net_account,
161+
number,
162+
amount.currency,
148163
)
149164
data.create_simple_posting(
150-
new_entry, net_account, -number, amount.currency
165+
new_entry,
166+
net_account,
167+
-number,
168+
amount.currency,
151169
)
152170

153171
return entries + new_entries, errors

fava_plugins/todo_as_error.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,29 @@
1212
Assets:Cash
1313
"""
1414

15+
from __future__ import annotations
16+
1517
import collections
18+
from typing import Any
19+
from typing import TYPE_CHECKING
1620

1721
from beancount.core.data import Transaction
1822

23+
if TYPE_CHECKING:
24+
from beancount.core.data import Directive
25+
1926
__plugins__ = [
2027
"todo_as_error",
2128
]
2229

2330
TodoError = collections.namedtuple("TodoError", "source message entry")
2431

2532

26-
def todo_as_error(entries, _):
33+
def todo_as_error(
34+
entries: list[Directive],
35+
_: Any,
36+
) -> tuple[list[Directive], list[TodoError]]:
2737
"""Create errors for entries 'todo' metadata."""
28-
2938
errors = []
3039

3140
for entry in entries:

pyproject.toml

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ name = "fava-plugins"
33
version = "1.0"
44
description = "A collection of Beancount plugins."
55
readme = "README.md"
6-
requires-python = ">=3.9"
7-
license = {text = "MIT License"}
6+
requires-python = ">=3.10"
7+
license = "MIT"
88
authors = [
99
{name = "Jakob Schnitzer", email = "mail@jakobschnitzer.de"}
1010
]
@@ -15,10 +15,8 @@ classifiers = [
1515
"Intended Audience :: End Users/Desktop",
1616
"Intended Audience :: Financial and Insurance Industry",
1717
"Intended Audience :: Information Technology",
18-
"License :: OSI Approved :: MIT License",
1918
"Natural Language :: English",
2019
"Programming Language :: Python :: 3 :: Only",
21-
"Programming Language :: Python :: 3.9",
2220
"Programming Language :: Python :: 3.10",
2321
"Programming Language :: Python :: 3.11",
2422
"Programming Language :: Python :: 3.12",
@@ -39,6 +37,49 @@ Issues = "https://github.com/beancount/fava-plugins/issues"
3937
requires = ["setuptools>=45", "wheel"]
4038
build-backend = "setuptools.build_meta"
4139

40+
[dependency-groups]
41+
# Type-checking with mypy.
42+
mypy = [
43+
"mypy>=1.14",
44+
"pytest>=8",
45+
]
46+
# Running pre-commit hooks.
47+
pre-commit = [
48+
"pre-commit>=4",
49+
]
50+
# Dependencies for tests.
51+
test = [
52+
"pytest>=8",
53+
]
54+
# Stuff for the dev environment.
55+
dev = [
56+
{ include-group = "mypy" },
57+
{ include-group = "pre-commit" },
58+
{ include-group = "test" },
59+
"ruff>=0.11",
60+
]
61+
62+
[tool.mypy]
63+
strict = true
64+
files = [
65+
"fava_plugins",
66+
"tests",
67+
]
68+
4269
[tool.ruff]
43-
target-version = "py39"
4470
line-length = 79
71+
72+
[tool.ruff.lint]
73+
extend-select = [
74+
"I",
75+
"TC",
76+
]
77+
78+
[tool.ruff.lint.flake8-type-checking]
79+
strict = true
80+
81+
[tool.ruff.lint.isort]
82+
force-single-line = true
83+
order-by-type = false
84+
known-first-party = ["fava_plugins"]
85+
required-imports = ["from __future__ import annotations"]

tests/conftest.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
from __future__ import annotations
2+
13
import pytest
4+
from beancount.core import data
25
from beancount.loader import load_string
6+
from beancount.loader import OptionsMap
7+
8+
LoaderResult = tuple[data.Directives, list[data.BeancountError], OptionsMap]
39

410

511
@pytest.fixture
6-
def load_doc(request):
12+
def load_doc(
13+
request: pytest.FixtureRequest,
14+
) -> LoaderResult:
715
return load_string(request.function.__doc__, dedent=True)

tests/test_split_income.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
15
from beancount.core import data
6+
from beancount.core.data import Transaction
27
from beancount.loader import load_string
38

9+
if TYPE_CHECKING:
10+
from beancount.core.data import Directive
11+
12+
from .conftest import LoaderResult
13+
414

5-
def _compare_postings(entry1, entry2):
15+
def _compare_postings(entry1: Directive, entry2: Directive) -> None:
16+
assert isinstance(entry1, Transaction)
17+
assert isinstance(entry2, Transaction)
618
amounts = {}
719
for pos in entry1.postings:
20+
assert pos.units
821
amounts[pos.account] = pos.units.number
922
for pos in entry2.postings:
23+
assert pos.units
1024
assert amounts[pos.account] == pos.units.number
1125

1226

13-
def test_split_income(load_doc):
27+
def test_split_income(load_doc: LoaderResult) -> None:
1428
"""
1529
plugin "fava_plugins.split_income" ""
1630
plugin "beancount.plugins.auto_accounts"
@@ -44,6 +58,7 @@ def test_split_income(load_doc):
4458
)
4559

4660
assert not errors
61+
assert isinstance(entries[8], Transaction)
4762
assert "pretax" in entries[8].tags
4863

4964
_compare_postings(entries[8], entries_after[1])
@@ -53,7 +68,7 @@ def test_split_income(load_doc):
5368
assert len([e for e in entries if isinstance(e, data.Open)]) == 7
5469

5570

56-
def test_split_income_config(load_doc):
71+
def test_split_income_config(load_doc: LoaderResult) -> None:
5772
"""
5873
plugin "fava_plugins.split_income" "{
5974
'income': 'Income:Work',
@@ -92,6 +107,7 @@ def test_split_income_config(load_doc):
92107
)
93108

94109
assert not errors
110+
assert isinstance(entries[8], Transaction)
95111
assert "brutto" in entries[8].tags
96112

97113
_compare_postings(entries[8], entries_after[1])
@@ -101,7 +117,7 @@ def test_split_income_config(load_doc):
101117
assert len([e for e in entries if isinstance(e, data.Open)]) == 7
102118

103119

104-
def test_split_income_mixed_currency_income(load_doc):
120+
def test_split_income_mixed_currency_income(load_doc: LoaderResult) -> None:
105121
"""
106122
plugin "fava_plugins.split_income" ""
107123
plugin "beancount.plugins.auto_accounts"
@@ -129,6 +145,7 @@ def test_split_income_mixed_currency_income(load_doc):
129145
)
130146

131147
assert not errors
148+
assert isinstance(entries[5], Transaction)
132149
assert "pretax" in entries[5].tags
133150

134151
_compare_postings(entries[5], entries_after[1])
@@ -138,7 +155,7 @@ def test_split_income_mixed_currency_income(load_doc):
138155
assert len([e for e in entries if isinstance(e, data.Open)]) == 4
139156

140157

141-
def test_split_income_mixed_currency_others(load_doc):
158+
def test_split_income_mixed_currency_others(load_doc: LoaderResult) -> None:
142159
"""
143160
plugin "fava_plugins.split_income" ""
144161
plugin "beancount.plugins.auto_accounts"
@@ -166,6 +183,7 @@ def test_split_income_mixed_currency_others(load_doc):
166183
)
167184

168185
assert not errors
186+
assert isinstance(entries[5], Transaction)
169187
assert "pretax" in entries[5].tags
170188

171189
_compare_postings(entries[5], entries_after[1])

0 commit comments

Comments
 (0)