Skip to content

Commit 389c099

Browse files
authored
uv, lint, ruff, etc (#71)
1 parent d287a41 commit 389c099

File tree

4 files changed

+2367
-204
lines changed

4 files changed

+2367
-204
lines changed

.pre-commit-config.yaml

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
repos:
22
- repo: https://github.com/charliermarsh/ruff-pre-commit
3-
rev: v0.0.256
3+
rev: v0.14.10
44
hooks:
5-
- id: ruff
5+
- id: ruff-check
66
args:
77
- --fix
8-
- repo: https://github.com/psf/black
9-
rev: 23.1.0
10-
hooks:
11-
- id: black
12-
args:
13-
- --quiet
14-
files: ^((custom_components|tests)/.+)?[^/]+\.py$
8+
- id: ruff-format
159
- repo: https://github.com/codespell-project/codespell
1610
rev: v2.2.2
1711
hooks:
@@ -21,15 +15,9 @@ repos:
2115
- --skip="./.*,*.csv,*.json"
2216
- --quiet-level=2
2317
exclude_types: [csv, json]
24-
- repo: https://github.com/PyCQA/isort
25-
rev: 5.12.0
26-
hooks:
27-
- id: isort
28-
- repo: https://github.com/pre-commit/mirrors-prettier
29-
rev: v2.7.1
30-
hooks:
31-
- id: prettier
3218
- repo: https://github.com/pre-commit/mirrors-mypy
33-
rev: v1.1.1
19+
rev: v1.19.1
3420
hooks:
3521
- id: mypy
22+
additional_dependencies:
23+
- types-dateparser

custom_components/intesisbox/intesisbox.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from __future__ import annotations
44

55
import asyncio
6-
from asyncio import BaseTransport, ensure_future
76
from collections.abc import Callable
87
import logging
98

@@ -36,6 +35,16 @@
3635

3736
NULL_VALUES = ["-32768", "32768"]
3837

38+
background_tasks = set()
39+
40+
41+
def ensure_background_task(coro):
42+
"""Ensure background task is running."""
43+
task = asyncio.ensure_future(coro)
44+
background_tasks.add(task)
45+
task.add_done_callback(background_tasks.discard)
46+
return task
47+
3948

4049
class IntesisBox(asyncio.Protocol):
4150
"""Handles communication with an intesisbox device via WMP."""
@@ -47,7 +56,7 @@ def __init__(self, ip: str, port: int = 3310, loop=None):
4756
self._mac = None
4857
self._device: dict[str, str] = {}
4958
self._connectionStatus = API_DISCONNECTED
50-
self._transport: BaseTransport | None = None
59+
self._transport: asyncio.BaseTransport | None = None
5160
self._updateCallbacks: list[Callable[[], None]] = []
5261
self._errorCallbacks: list[Callable[[str], None]] = []
5362
self._errorMessage: str | None = None
@@ -65,11 +74,11 @@ def __init__(self, ip: str, port: int = 3310, loop=None):
6574
self._setpoint_minimum: int | None = None
6675
self._setpoint_maximum: int | None = None
6776

68-
def connection_made(self, transport: BaseTransport):
77+
def connection_made(self, transport: asyncio.BaseTransport):
6978
"""Asyncio callback for a successful connection."""
7079
_LOGGER.debug("Connected to IntesisBox")
7180
self._transport = transport
72-
_ = asyncio.ensure_future(self.query_initial_state())
81+
ensure_background_task(self.query_initial_state())
7382

7483
async def keep_alive(self):
7584
"""Send a keepalive command to reset it's watchdog timer."""
@@ -128,8 +137,8 @@ def data_received(self, data):
128137
if cmd == "ID":
129138
self._parse_id_received(args)
130139
self._connectionStatus = API_AUTHENTICATED
131-
_ = asyncio.ensure_future(self.poll_status())
132-
_ = asyncio.ensure_future(self.poll_ambtemp())
140+
ensure_background_task(self.poll_status())
141+
ensure_background_task(self.poll_ambtemp())
133142
elif cmd == "CHN,1":
134143
self._parse_change_received(args)
135144
statusChanged = True
@@ -216,7 +225,7 @@ def connect(self):
216225
_LOGGER.debug(
217226
"Opening connection to IntesisBox %s:%s", self._ip, self._port
218227
)
219-
_ = ensure_future(coro, loop=self._eventLoop)
228+
ensure_background_task(coro)
220229
else:
221230
_LOGGER.debug("Missing IP address or port.")
222231
self._connectionStatus = API_DISCONNECTED

pyproject.toml

Lines changed: 28 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,191 +1,33 @@
1-
[tool.poetry]
1+
[project]
22
name = "intesisbox"
33
version = "0.0.0"
44
description = "Home Assistant integration for Intesisbox devices"
55
authors = ["Your Name <you@example.com>"]
66
readme = "README.md"
7-
8-
[tool.poetry.dependencies]
9-
python = "^3.11"
10-
homeassistant = "^2023.3.6"
11-
12-
13-
[build-system]
14-
requires = ["poetry-core"]
15-
build-backend = "poetry.core.masonry.api"
16-
17-
[tool.black]
18-
extend-exclude = "/generated/"
19-
20-
[tool.isort]
21-
# https://github.com/PyCQA/isort/wiki/isort-Settings
22-
profile = "black"
23-
# will group `import x` and `from x import` of the same module.
24-
force_sort_within_sections = true
25-
known_first_party = [
26-
"homeassistant",
27-
"tests",
28-
]
29-
forced_separate = [
30-
"tests",
31-
]
32-
combine_as_imports = true
33-
34-
[tool.pylint.MAIN]
35-
py-version = "3.10"
36-
ignore = [
37-
"tests",
38-
]
39-
# Use a conservative default here; 2 should speed up most setups and not hurt
40-
# any too bad. Override on command line as appropriate.
41-
jobs = 2
42-
init-hook = """\
43-
from pathlib import Path; \
44-
import sys; \
45-
from pylint.config import find_default_config_files; \
46-
sys.path.append( \
47-
str(Path(next(find_default_config_files())).parent.joinpath('pylint/plugins'))
48-
) \
49-
"""
50-
load-plugins = [
51-
"pylint.extensions.code_style",
52-
"pylint.extensions.typing",
53-
"hass_enforce_type_hints",
54-
"hass_imports",
55-
"hass_logger",
56-
"pylint_per_file_ignores",
57-
]
58-
persistent = false
59-
extension-pkg-allow-list = [
60-
"av.audio.stream",
61-
"av.stream",
62-
"ciso8601",
63-
"orjson",
64-
"cv2",
65-
]
66-
fail-on = [
67-
"I",
68-
]
69-
70-
[tool.pylint.BASIC]
71-
class-const-naming-style = "any"
72-
good-names = [
73-
"_",
74-
"ev",
75-
"ex",
76-
"fp",
77-
"i",
78-
"id",
79-
"j",
80-
"k",
81-
"Run",
82-
"ip",
83-
]
84-
85-
[tool.pylint."MESSAGES CONTROL"]
86-
# Reasons disabled:
87-
# format - handled by black
88-
# locally-disabled - it spams too much
89-
# duplicate-code - unavoidable
90-
# cyclic-import - doesn't test if both import on load
91-
# abstract-class-little-used - prevents from setting right foundation
92-
# unused-argument - generic callbacks and setup methods create a lot of warnings
93-
# too-many-* - are not enforced for the sake of readability
94-
# too-few-* - same as too-many-*
95-
# abstract-method - with intro of async there are always methods missing
96-
# inconsistent-return-statements - doesn't handle raise
97-
# too-many-ancestors - it's too strict.
98-
# wrong-import-order - isort guards this
99-
# consider-using-f-string - str.format sometimes more readable
100-
# ---
101-
# Pylint CodeStyle plugin
102-
# consider-using-namedtuple-or-dataclass - too opinionated
103-
# consider-using-assignment-expr - decision to use := better left to devs
104-
disable = [
105-
"format",
106-
"abstract-method",
107-
"cyclic-import",
108-
"duplicate-code",
109-
"inconsistent-return-statements",
110-
"locally-disabled",
111-
"not-context-manager",
112-
"too-few-public-methods",
113-
"too-many-ancestors",
114-
"too-many-arguments",
115-
"too-many-branches",
116-
"too-many-instance-attributes",
117-
"too-many-lines",
118-
"too-many-locals",
119-
"too-many-public-methods",
120-
"too-many-return-statements",
121-
"too-many-statements",
122-
"too-many-boolean-expressions",
123-
"unused-argument",
124-
"wrong-import-order",
125-
"consider-using-f-string",
126-
"consider-using-namedtuple-or-dataclass",
127-
"consider-using-assignment-expr",
128-
]
129-
enable = [
130-
#"useless-suppression", # temporarily every now and then to clean them up
131-
"use-symbolic-message-instead",
7+
requires-python = ">=3.13.2, <3.14"
8+
dependencies = [
9+
"homeassistant>=2025.12.4",
13210
]
13311

134-
[tool.pylint.REPORTS]
135-
score = false
136-
137-
[tool.pylint.TYPECHECK]
138-
ignored-classes = [
139-
"_CountingAttr", # for attrs
140-
]
141-
mixin-class-rgx = ".*[Mm]ix[Ii]n"
142-
143-
[tool.pylint.FORMAT]
144-
expected-line-ending-format = "LF"
145-
146-
[tool.pylint.EXCEPTIONS]
147-
overgeneral-exceptions = [
148-
"builtins.BaseException",
149-
"builtins.Exception",
150-
# "homeassistant.exceptions.HomeAssistantError", # too many issues
151-
]
152-
153-
[tool.pylint.TYPING]
154-
runtime-typing = false
155-
156-
[tool.pylint.CODE_STYLE]
157-
max-line-length-suggestions = 72
158-
159-
[tool.pylint-per-file-ignores]
160-
# hass-component-root-import: Tests test non-public APIs
161-
# protected-access: Tests do often test internals a lot
162-
# redefined-outer-name: Tests reference fixtures in the test function
163-
"/tests/"="hass-component-root-import,protected-access,redefined-outer-name"
164-
16512
[tool.pytest.ini_options]
166-
testpaths = [
167-
"tests",
168-
]
169-
norecursedirs = [
170-
".git",
171-
"testing_config",
172-
]
17313
log_format = "%(asctime)s.%(msecs)03d %(levelname)-8s %(threadName)s %(name)s:%(filename)s:%(lineno)s %(message)s"
17414
log_date_format = "%Y-%m-%d %H:%M:%S"
17515
asyncio_mode = "auto"
176-
filterwarnings = ["error::sqlalchemy.exc.SAWarning"]
16+
asyncio_default_fixture_loop_scope = "function"
17+
filterwarnings = []
17718

17819
[tool.ruff]
179-
target-version = "py310"
20+
target-version = "py313"
18021

22+
[tool.ruff.lint]
18123
select = [
18224
"B007", # Loop control variable {name} not used within loop body
18325
"B014", # Exception handler with duplicate exception
18426
"C", # complexity
18527
"D", # docstrings
18628
"E", # pycodestyle
18729
"F", # pyflakes/autoflake
188-
"ICN001", # import concentions; {name} should be imported as {asname}
30+
"I", # isort
18931
"PGH004", # Use specific rule codes when using noqa
19032
"PLC0414", # Useless import alias. Import alias does not rename original package.
19133
"SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass
@@ -210,30 +52,37 @@ ignore = [
21052
"D407", # Section name underlining
21153
"E501", # line too long
21254
"E731", # do not assign a lambda expression, use a def
213-
# Ignored due to performance: https://github.com/charliermarsh/ruff/issues/2923
214-
"UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)`
21555
]
21656

217-
[tool.ruff.flake8-import-conventions.extend-aliases]
57+
[tool.ruff.lint.flake8-import-conventions.extend-aliases]
21858
voluptuous = "vol"
21959
"homeassistant.helpers.area_registry" = "ar"
22060
"homeassistant.helpers.config_validation" = "cv"
22161
"homeassistant.helpers.device_registry" = "dr"
22262
"homeassistant.helpers.entity_registry" = "er"
22363
"homeassistant.helpers.issue_registry" = "ir"
22464

225-
[tool.ruff.flake8-pytest-style]
65+
[tool.ruff.lint.flake8-pytest-style]
22666
fixture-parentheses = false
22767

228-
[tool.ruff.pyupgrade]
68+
[tool.ruff.lint.pyupgrade]
22969
keep-runtime-typing = true
23070

231-
[tool.ruff.per-file-ignores]
71+
[tool.ruff.lint.mccabe]
72+
max-complexity = 25
23273

233-
# Allow for main entry & scripts to write to stdout
234-
"homeassistant/__main__.py" = ["T201"]
235-
"homeassistant/scripts/*" = ["T201"]
236-
"script/*" = ["T20"]
74+
[tool.ruff.lint.per-file-ignores]
75+
"tests/*" = [
76+
"D", # docstrings
77+
]
23778

238-
[tool.ruff.mccabe]
239-
max-complexity = 25
79+
[tool.ruff.lint.isort]
80+
force-sort-within-sections = true
81+
known-first-party = [
82+
"homeassistant",
83+
"tests",
84+
]
85+
forced-separate = [
86+
"tests",
87+
]
88+
combine-as-imports = true

0 commit comments

Comments
 (0)