Skip to content

Commit 2b080e6

Browse files
committed
Migrate to uv
1 parent 1d0a33f commit 2b080e6

8 files changed

Lines changed: 901 additions & 149 deletions

File tree

.github/dependabot.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ updates:
66
schedule:
77
interval: "weekly"
88

9-
- package-ecosystem: "pip"
9+
- package-ecosystem: "uv"
1010
directory: "/"
1111
schedule:
1212
interval: "weekly"

.github/workflows/pytest.yml

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,14 @@ jobs:
1313
build:
1414
runs-on: ubuntu-latest
1515
steps:
16-
- uses: actions/checkout@v4
17-
- name: Set up Python
18-
uses: actions/setup-python@v5
19-
with:
20-
python-version: '3.12'
21-
cache: 'pip'
22-
- name: Install dependencies
23-
run: |
24-
python -m pip install --upgrade pip
25-
pip install -e .[test]
26-
pip install -e .[lint]
27-
- name: Lint with Ruff
28-
run: |
29-
ruff check --output-format=github .
30-
- name: Test with pytest and coverage
31-
run: |
32-
pytest --cov=./ --cov-report=term
16+
- uses: actions/checkout@v4
17+
- name: Set up uv
18+
uses: astral-sh/setup-uv@v5
19+
with:
20+
enable-cache: true
21+
- name: Install dependencies
22+
run: uv sync --locked --all-extras
23+
- name: Lint with Ruff
24+
run: uv run ruff check --output-format=github .
25+
- name: Test with pytest and coverage
26+
run: uv run pytest --cov=./ --cov-report=term

.github/workflows/python-publish.yml

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,17 @@ jobs:
2020
runs-on: ubuntu-latest
2121

2222
steps:
23-
- uses: actions/checkout@v4
23+
- uses: actions/checkout@v6
2424

25-
- uses: actions/setup-python@v5
25+
- uses: astral-sh/setup-uv@v5
2626
with:
27-
python-version: "3.x"
27+
enable-cache: true
2828

2929
- name: Build release distributions
30-
run: |
31-
32-
python -m pip install build
33-
python -m build
30+
run: uv build
3431

3532
- name: Upload distributions
36-
uses: actions/upload-artifact@v4
33+
uses: actions/upload-artifact@v7
3734
with:
3835
name: release-dists
3936
path: dist/
@@ -53,13 +50,13 @@ jobs:
5350
url: https://pypi.org/p/kitchenowl-python
5451

5552
steps:
53+
- uses: astral-sh/setup-uv@v5
54+
5655
- name: Retrieve release distributions
57-
uses: actions/download-artifact@v4
56+
uses: actions/download-artifact@v8
5857
with:
5958
name: release-dists
6059
path: dist/
6160

6261
- name: Publish release distributions to PyPI
63-
uses: pypa/gh-action-pypi-publish@release/v1
64-
with:
65-
packages-dir: dist/
62+
run: uv publish --trusted-publishing always dist/*

pyproject.toml

Lines changed: 101 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,42 @@ readme = "README.md"
55
keywords = ["kitchenowl"]
66
version = "0.0.2"
77
authors = [
8-
{name = "Tom Bursch", email = "tom@kitchenowl.org"},
9-
{name = "super-qua"},
8+
{ name = "Tom Bursch", email = "tom@kitchenowl.org" },
9+
{ name = "super-qua" },
1010
]
11-
license = {file = "LICENSE"}
11+
license = { file = "LICENSE" }
1212
classifiers = [
13-
"Development Status :: 3 - Alpha",
14-
"Programming Language :: Python"
13+
"Development Status :: 3 - Alpha",
14+
"Programming Language :: Python",
1515
]
16-
dependencies = [
17-
"aiohttp >= 3.11.11"
18-
]
19-
requires-python = ">=3.8"
16+
dependencies = ["aiohttp >= 3.11.11"]
17+
requires-python = ">=3.12"
2018

2119
[project.optional-dependencies]
2220
test = [
23-
"aioresponses == 0.7.8",
24-
"pytest == 8.3.4",
25-
"pytest-asyncio == 0.25.3",
26-
"pytest-cov == 6.0.0",
27-
"syrupy == 4.8.1"
28-
]
29-
lint = [
30-
"ruff == 0.9.4"
21+
"aioresponses >= 0.7.8",
22+
"pytest >= 8.3.4",
23+
"pytest-asyncio >= 0.25.3",
24+
"pytest-cov >= 6.0.0",
25+
"syrupy >= 4.8.1",
3126
]
27+
lint = ["ruff >= 0.9.4"]
3228

3329
[project.urls]
3430
Homepage = "https://kitchenowl.org"
3531
Repository = "https://github.com/tombursch/kitchenowl-python"
3632
Issues = "https://github.com/TomBursch/kitchenowl/issues"
3733
Changelog = "https://github.com/tombursch/kitchenowl-python/blob/main/CHANGELOG.md"
3834

35+
[build-system]
36+
requires = ["uv_build>=0.11.3,<0.12"]
37+
build-backend = "uv_build"
3938

4039
[tool.pytest.ini_options]
4140
minversion = 6.0
4241
pythonpath = "src"
4342
asyncio_mode = "auto"
44-
markers = [
45-
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
46-
]
43+
markers = ["slow: marks tests as slow (deselect with '-m \"not slow\"')"]
4744

4845
[tool.ruff]
4946
line-length = 100
@@ -57,87 +54,86 @@ line-ending = "auto"
5754
[tool.ruff.lint]
5855
# linter rules copied from https://github.com/home-assistant/core/blob/dev/pyproject.toml
5956
select = [
60-
"A001", # Variable {name} is shadowing a Python builtin
57+
"A001", # Variable {name} is shadowing a Python builtin
6158
"ASYNC210", # Async functions should not call blocking HTTP methods
6259
"ASYNC220", # Async functions should not create subprocesses with blocking methods
6360
"ASYNC221", # Async functions should not run processes with blocking methods
6461
"ASYNC222", # Async functions should not wait on processes with blocking methods
6562
"ASYNC230", # Async functions should not open files with blocking methods like open
6663
"ASYNC251", # Async functions should not call time.sleep
67-
"B002", # Python does not support the unary prefix increment
68-
"B005", # Using .strip() with multi-character strings is misleading
69-
"B007", # Loop control variable {name} not used within loop body
70-
"B014", # Exception handler with duplicate exception
71-
"B015", # Pointless comparison. Did you mean to assign a value? Otherwise, prepend assert or remove it.
72-
"B017", # pytest.raises(BaseException) should be considered evil
73-
"B018", # Found useless attribute access. Either assign it to a variable or remove it.
74-
"B023", # Function definition does not bind loop variable {name}
75-
"B026", # Star-arg unpacking after a keyword argument is strongly discouraged
76-
"B032", # Possible unintentional type annotation (using :). Did you mean to assign (using =)?
77-
"B904", # Use raise from to specify exception cause
78-
"B905", # zip() without an explicit strict= parameter
64+
"B002", # Python does not support the unary prefix increment
65+
"B005", # Using .strip() with multi-character strings is misleading
66+
"B007", # Loop control variable {name} not used within loop body
67+
"B014", # Exception handler with duplicate exception
68+
"B015", # Pointless comparison. Did you mean to assign a value? Otherwise, prepend assert or remove it.
69+
"B017", # pytest.raises(BaseException) should be considered evil
70+
"B018", # Found useless attribute access. Either assign it to a variable or remove it.
71+
"B023", # Function definition does not bind loop variable {name}
72+
"B026", # Star-arg unpacking after a keyword argument is strongly discouraged
73+
"B032", # Possible unintentional type annotation (using :). Did you mean to assign (using =)?
74+
"B904", # Use raise from to specify exception cause
75+
"B905", # zip() without an explicit strict= parameter
7976
"BLE",
80-
"C", # complexity
81-
"COM818", # Trailing comma on bare tuple prohibited
82-
"D", # docstrings
83-
"DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow()
84-
"DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts)
85-
"E", # pycodestyle
86-
"F", # pyflakes/autoflake
87-
"FLY", # flynt
88-
"FURB", # refurb
89-
"G", # flake8-logging-format
90-
"I", # isort
91-
"INP", # flake8-no-pep420
92-
"ISC", # flake8-implicit-str-concat
93-
"ICN001", # import concentions; {name} should be imported as {asname}
94-
"LOG", # flake8-logging
95-
"N804", # First argument of a class method should be named cls
96-
"N805", # First argument of a method should be named self
97-
"N815", # Variable {name} in class scope should not be mixedCase
98-
"PERF", # Perflint
99-
"PGH", # pygrep-hooks
100-
"PIE", # flake8-pie
101-
"PL", # pylint
102-
"PT", # flake8-pytest-style
103-
"PYI", # flake8-pyi
104-
"RET", # flake8-return
105-
"RSE", # flake8-raise
106-
"RUF005", # Consider iterable unpacking instead of concatenation
107-
"RUF006", # Store a reference to the return value of asyncio.create_task
108-
"RUF010", # Use explicit conversion flag
109-
"RUF013", # PEP 484 prohibits implicit Optional
110-
"RUF017", # Avoid quadratic list summation
111-
"RUF018", # Avoid assignment expressions in assert statements
112-
"RUF019", # Unnecessary key check before dictionary access
77+
"C", # complexity
78+
"COM818", # Trailing comma on bare tuple prohibited
79+
"D", # docstrings
80+
"DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow()
81+
"DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts)
82+
"E", # pycodestyle
83+
"F", # pyflakes/autoflake
84+
"FLY", # flynt
85+
"FURB", # refurb
86+
"G", # flake8-logging-format
87+
"I", # isort
88+
"INP", # flake8-no-pep420
89+
"ISC", # flake8-implicit-str-concat
90+
"ICN001", # import concentions; {name} should be imported as {asname}
91+
"LOG", # flake8-logging
92+
"N804", # First argument of a class method should be named cls
93+
"N805", # First argument of a method should be named self
94+
"N815", # Variable {name} in class scope should not be mixedCase
95+
"PERF", # Perflint
96+
"PGH", # pygrep-hooks
97+
"PIE", # flake8-pie
98+
"PL", # pylint
99+
"PT", # flake8-pytest-style
100+
"PYI", # flake8-pyi
101+
"RET", # flake8-return
102+
"RSE", # flake8-raise
103+
"RUF005", # Consider iterable unpacking instead of concatenation
104+
"RUF006", # Store a reference to the return value of asyncio.create_task
105+
"RUF010", # Use explicit conversion flag
106+
"RUF013", # PEP 484 prohibits implicit Optional
107+
"RUF017", # Avoid quadratic list summation
108+
"RUF018", # Avoid assignment expressions in assert statements
109+
"RUF019", # Unnecessary key check before dictionary access
113110
# "RUF100", # Unused `noqa` directive; temporarily every now and then to clean them up
114-
"S102", # Use of exec detected
115-
"S103", # bad-file-permissions
116-
"S108", # hardcoded-temp-file
117-
"S306", # suspicious-mktemp-usage
118-
"S307", # suspicious-eval-usage
119-
"S313", # suspicious-xmlc-element-tree-usage
120-
"S314", # suspicious-xml-element-tree-usage
121-
"S315", # suspicious-xml-expat-reader-usage
122-
"S316", # suspicious-xml-expat-builder-usage
123-
"S317", # suspicious-xml-sax-usage
124-
"S318", # suspicious-xml-mini-dom-usage
125-
"S319", # suspicious-xml-pull-dom-usage
126-
"S320", # suspicious-xmle-tree-usage
127-
"S601", # paramiko-call
128-
"S602", # subprocess-popen-with-shell-equals-true
129-
"S604", # call-with-shell-equals-true
130-
"S608", # hardcoded-sql-expression
131-
"S609", # unix-command-wildcard-injection
132-
"SIM", # flake8-simplify
133-
"SLF", # flake8-self
134-
"SLOT", # flake8-slots
135-
"T100", # Trace found: {name} used
136-
"T20", # flake8-print
111+
"S102", # Use of exec detected
112+
"S103", # bad-file-permissions
113+
"S108", # hardcoded-temp-file
114+
"S306", # suspicious-mktemp-usage
115+
"S307", # suspicious-eval-usage
116+
"S313", # suspicious-xmlc-element-tree-usage
117+
"S314", # suspicious-xml-element-tree-usage
118+
"S315", # suspicious-xml-expat-reader-usage
119+
"S316", # suspicious-xml-expat-builder-usage
120+
"S317", # suspicious-xml-sax-usage
121+
"S318", # suspicious-xml-mini-dom-usage
122+
"S319", # suspicious-xml-pull-dom-usage
123+
"S601", # paramiko-call
124+
"S602", # subprocess-popen-with-shell-equals-true
125+
"S604", # call-with-shell-equals-true
126+
"S608", # hardcoded-sql-expression
127+
"S609", # unix-command-wildcard-injection
128+
"SIM", # flake8-simplify
129+
"SLF", # flake8-self
130+
"SLOT", # flake8-slots
131+
"T100", # Trace found: {name} used
132+
"T20", # flake8-print
137133
"TID251", # Banned imports
138-
"TRY", # tryceratops
139-
"UP", # pyupgrade
140-
"W", # pycodestyle
134+
"TRY", # tryceratops
135+
"UP", # pyupgrade
136+
"W", # pycodestyle
141137
]
142138

143139
ignore = [
@@ -155,21 +151,18 @@ ignore = [
155151
"PLR0915", # Too many statements ({statements} > {max_statements})
156152
"PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable
157153
"PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target
158-
"PT004", # Fixture {fixture} does not return anything, add leading underscore
159-
"PT011", # pytest.raises({exception}) is too broad, set the `match` parameter or use a more specific exception
160-
"PT018", # Assertion should be broken down into multiple parts
161-
"RUF001", # String contains ambiguous unicode character.
162-
"RUF002", # Docstring contains ambiguous unicode character.
163-
"RUF003", # Comment contains ambiguous unicode character.
164-
"RUF015", # Prefer next(...) over single element slice
165-
"SIM102", # Use a single if statement instead of nested if statements
166-
"SIM103", # Return the condition {condition} directly
167-
"SIM108", # Use ternary operator {contents} instead of if-else-block
168-
"SIM115", # Use context handler for opening files
169-
"TRY003", # Avoid specifying long messages outside the exception class
170-
"TRY400", # Use `logging.exception` instead of `logging.error`
171-
# Ignored due to performance: https://github.com/charliermarsh/ruff/issues/2923
172-
"UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)`
154+
"PT011", # pytest.raises({exception}) is too broad, set the `match` parameter or use a more specific exception
155+
"PT018", # Assertion should be broken down into multiple parts
156+
"RUF001", # String contains ambiguous unicode character.
157+
"RUF002", # Docstring contains ambiguous unicode character.
158+
"RUF003", # Comment contains ambiguous unicode character.
159+
"RUF015", # Prefer next(...) over single element slice
160+
"SIM102", # Use a single if statement instead of nested if statements
161+
"SIM103", # Return the condition {condition} directly
162+
"SIM108", # Use ternary operator {contents} instead of if-else-block
163+
"SIM115", # Use context handler for opening files
164+
"TRY003", # Avoid specifying long messages outside the exception class
165+
"TRY400", # Use `logging.exception` instead of `logging.error`
173166

174167
# May conflict with the formatter, https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
175168
"W191",
@@ -188,7 +181,7 @@ ignore = [
188181

189182
# temporarily disabled
190183
"RET503",
191-
"TRY301"
184+
"TRY301",
192185
]
193186

194187
[tool.ruff.lint.pydocstyle]

src/kitchenowl_python/py.typed

Whitespace-only changes.

src/kitchenowl_python/types.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# TypedDict for now as it allows for changes in the API return values
44

5-
from typing import List, NotRequired, TypedDict
5+
from typing import NotRequired, TypedDict
66

77

88
class KitchenOwlShoppingListCategory(TypedDict):
@@ -73,21 +73,21 @@ class KitchenOwlHousehold(TypedDict):
7373
expenses_feature: bool
7474
id: int
7575
language: str
76-
member: List[KitchenOwlUser]
76+
member: list[KitchenOwlUser]
7777
name: str
7878
photo: str | None
7979
planner_feature: bool
8080
updated_at: int
81-
view_ordering: List[str]
81+
view_ordering: list[str]
8282

8383

84-
class KitchenOwlHouseholdsResponse(List[KitchenOwlHousehold]):
84+
class KitchenOwlHouseholdsResponse(list[KitchenOwlHousehold]):
8585
"""The households response from KitchenOwl."""
8686

8787

88-
class KitchenOwlShoppingListsResponse(List[KitchenOwlShoppingList]):
88+
class KitchenOwlShoppingListsResponse(list[KitchenOwlShoppingList]):
8989
"""The shopping lists response from KitchenOwl."""
9090

9191

92-
class KitchenOwlShoppingListItemsResponse(List[KitchenOwlShoppingListItem]):
92+
class KitchenOwlShoppingListItemsResponse(list[KitchenOwlShoppingListItem]):
9393
"""The response for shopping list items from KitchenOwl."""

0 commit comments

Comments
 (0)