Skip to content

Commit

Permalink
Fixes for HA mypy, add more lint rules (#639)
Browse files Browse the repository at this point in the history
* Update MyPy for HA

* Add more rules to ruff
  • Loading branch information
rikroe authored Jul 27, 2024
1 parent 30246ac commit 812aa5a
Show file tree
Hide file tree
Showing 13 changed files with 120 additions and 131 deletions.
11 changes: 4 additions & 7 deletions bimmer_connected/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import argparse
import asyncio
import contextlib
import json
import logging
import sys
Expand Down Expand Up @@ -250,10 +251,8 @@ async def image(account: MyBMWAccount, args) -> None:
for viewdirection in VehicleViewDirection:
if viewdirection == VehicleViewDirection.UNKNOWN:
continue
filename = str(viewdirection.name).lower() + ".png"
with open(filename, "wb") as output_file:
image_data = await vehicle.get_vehicle_image(viewdirection)
output_file.write(image_data)
filename = (Path.cwd() / str(viewdirection.name).lower()).with_suffix(".png")
await asyncio.to_thread(filename.write_bytes, await vehicle.get_vehicle_image(viewdirection))
print(f"vehicle image saved to {filename}")


Expand Down Expand Up @@ -336,10 +335,8 @@ def main():
account.set_observer_position(args.lat, args.lng)

if args.oauth_store.exists():
try:
with contextlib.suppress(json.JSONDecodeError):
account.set_refresh_token(**json.loads(args.oauth_store.read_text()))
except json.JSONDecodeError:
pass

loop = asyncio.get_event_loop()
try:
Expand Down
6 changes: 3 additions & 3 deletions bimmer_connected/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def _missing_(cls, value):
return member
if has_unknown:
_LOGGER.warning("'%s' is not a valid '%s'", value, cls.__name__)
return getattr(cls, "UNKNOWN")
return cls.UNKNOWN
raise ValueError(f"'{value}' is not a valid {cls.__name__}")


Expand Down Expand Up @@ -117,7 +117,7 @@ class PointOfInterest:

lat: InitVar[float]
lon: InitVar[float]
name: InitVar[str] = DEFAULT_POI_NAME
name: InitVar[Optional[str]] = DEFAULT_POI_NAME
street: InitVar[str] = None
postal_code: InitVar[str] = None
city: InitVar[str] = None
Expand All @@ -130,7 +130,7 @@ class PointOfInterest:
entrances: Optional[List] = field(init=False)
placeType: Optional[str] = "ADDRESS"
category: Dict[str, Optional[str]] = field(init=False)
title: str = "Sent with ♥ by bimmer_connected"
title: Optional[str] = DEFAULT_POI_NAME

# The following attributes are not by us but available in the API
provider: Optional[str] = None
Expand Down
44 changes: 19 additions & 25 deletions bimmer_connected/tests/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ async def test_vehicles(bmw_fixture: respx.Router):

vehicle = account.get_vehicle(VIN_G26)
assert vehicle is not None
assert VIN_G26 == vehicle.vin
assert vehicle.vin == VIN_G26

assert account.get_vehicle("invalid_vin") is None

Expand Down Expand Up @@ -425,9 +425,8 @@ async def test_429_retry_raise_login(caplog, bmw_fixture: respx.Router):
bmw_fixture.get("/eadrax-ucs/v1/presentation/oauth/config").mock(return_value=httpx.Response(429, json=json_429))
caplog.set_level(logging.DEBUG)

with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock):
with pytest.raises(MyBMWAPIError):
await account.get_vehicles()
with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock), pytest.raises(MyBMWAPIError):
await account.get_vehicles()

log_429 = [
r
Expand Down Expand Up @@ -477,9 +476,8 @@ async def test_429_retry_raise_vehicles(caplog, bmw_fixture: respx.Router):
bmw_fixture.post(VEHICLES_URL).mock(return_value=httpx.Response(429, json=json_429))
caplog.set_level(logging.DEBUG)

with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock):
with pytest.raises(MyBMWQuotaError):
await account.get_vehicles()
with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock), pytest.raises(MyBMWQuotaError):
await account.get_vehicles()

log_429 = [
r
Expand Down Expand Up @@ -530,9 +528,8 @@ async def test_429_retry_with_login_raise_vehicles(bmw_fixture: respx.Router):
]
)

with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock):
with pytest.raises(MyBMWQuotaError):
await account.get_vehicles()
with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock), pytest.raises(MyBMWQuotaError):
await account.get_vehicles()


@pytest.mark.asyncio
Expand All @@ -547,9 +544,8 @@ async def test_multiple_401(bmw_fixture: respx.Router):
]
)

with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock):
with pytest.raises(MyBMWAuthError):
await account.get_vehicles()
with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock), pytest.raises(MyBMWAuthError):
await account.get_vehicles()


@pytest.mark.asyncio
Expand Down Expand Up @@ -594,9 +590,8 @@ async def test_401_after_429_fail(bmw_fixture: respx.Router):
]
)

with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock):
with pytest.raises(MyBMWQuotaError):
await account.get_vehicles()
with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock), pytest.raises(MyBMWQuotaError):
await account.get_vehicles()


@pytest.mark.asyncio
Expand All @@ -614,9 +609,8 @@ async def test_403_quota_exceeded_vehicles_usa(caplog, bmw_fixture: respx.Router
)
caplog.set_level(logging.DEBUG)

with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock):
with pytest.raises(MyBMWQuotaError):
await account.get_vehicles()
with mock.patch("asyncio.sleep", new_callable=mock.AsyncMock), pytest.raises(MyBMWQuotaError):
await account.get_vehicles()

log_quota = [r for r in caplog.records if "quota" in r.message]
assert len(log_quota) == 1
Expand Down Expand Up @@ -671,13 +665,13 @@ async def test_no_vehicle_details(caplog, bmw_fixture: respx.Router):
async def test_client_async_only(bmw_fixture: respx.Router):
"""Test that the Authentication providers only work async."""

with httpx.Client(auth=MyBMWAuthentication(TEST_USERNAME, TEST_PASSWORD, TEST_REGION)) as client:
with pytest.raises(RuntimeError):
client.get("/eadrax-ucs/v1/presentation/oauth/config")
with httpx.Client(auth=MyBMWAuthentication(TEST_USERNAME, TEST_PASSWORD, TEST_REGION)) as client, pytest.raises(
RuntimeError
):
client.get("/eadrax-ucs/v1/presentation/oauth/config")

with httpx.Client(auth=MyBMWLoginRetry()) as client:
with pytest.raises(RuntimeError):
client.get("/eadrax-ucs/v1/presentation/oauth/config")
with httpx.Client(auth=MyBMWLoginRetry()) as client, pytest.raises(RuntimeError):
client.get("/eadrax-ucs/v1/presentation/oauth/config")


@pytest.mark.asyncio
Expand Down
2 changes: 1 addition & 1 deletion bimmer_connected/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

def test_valid_regions():
"""Test valid regions."""
assert ["north_america", "china", "rest_of_world"] == valid_regions()
assert valid_regions() == ["north_america", "china", "rest_of_world"]


def test_unknown_region():
Expand Down
9 changes: 3 additions & 6 deletions bimmer_connected/tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import contextlib
import json
import subprocess
import sys
Expand Down Expand Up @@ -48,10 +49,8 @@ def test_status_json_filtered(capsys: pytest.CaptureFixture, vin, expected_count
"""Test the status command JSON output filtered by VIN."""

sys.argv = ["bimmerconnected", "status", "-j", "-v", vin, *ARGS_USER_PW_REGION]
try:
with contextlib.suppress(SystemExit):
bimmer_connected.cli.main()
except SystemExit:
pass
result = capsys.readouterr()

if expected_count == 1:
Expand Down Expand Up @@ -90,10 +89,8 @@ def test_status_filtered(capsys: pytest.CaptureFixture, vin, expected_count):
"""Test the status command text output filtered by VIN."""

sys.argv = ["bimmerconnected", "status", "-v", vin, *ARGS_USER_PW_REGION]
try:
with contextlib.suppress(SystemExit):
bimmer_connected.cli.main()
except SystemExit:
pass
result = capsys.readouterr()

assert f"Found {get_fingerprint_count('states')} vehicles" in result.out
Expand Down
24 changes: 12 additions & 12 deletions bimmer_connected/tests/test_remote_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@
def test_states():
"""Test parsing the different response types."""
rss = RemoteServiceStatus(load_response(REMOTE_SERVICE_RESPONSE_PENDING))
assert ExecutionState.PENDING == rss.state
assert rss.state == ExecutionState.PENDING

rss = RemoteServiceStatus(load_response(REMOTE_SERVICE_RESPONSE_DELIVERED))
assert ExecutionState.DELIVERED == rss.state
assert rss.state == ExecutionState.DELIVERED

rss = RemoteServiceStatus(load_response(REMOTE_SERVICE_RESPONSE_EXECUTED))
assert ExecutionState.EXECUTED == rss.state
assert rss.state == ExecutionState.EXECUTED


ALL_SERVICES = {
Expand Down Expand Up @@ -82,7 +82,7 @@ async def test_trigger_remote_services(bmw_fixture: respx.Router):
response = await getattr(vehicle.remote_services, service["call"])( # type: ignore[call-overload]
*service.get("args", []), **service.get("kwargs", {})
)
assert ExecutionState.EXECUTED == response.state
assert response.state == ExecutionState.EXECUTED

if service["refresh"]:
mock_listener.assert_called_once_with()
Expand Down Expand Up @@ -309,18 +309,18 @@ async def test_get_remote_position(bmw_fixture: respx.Router):
location = vehicle.vehicle_location

# Check original position
assert (48.177334, 11.556274) == location.location
assert 180 == location.heading
assert location.location == (48.177334, 11.556274)
assert location.heading == 180

# Check updated position
await vehicle.remote_services.trigger_remote_vehicle_finder()
assert (123.456, 34.5678) == location.location
assert 121 == location.heading
assert location.location == (123.456, 34.5678)
assert location.heading == 121

# Position should still be from vehicle finder after status update
await account.get_vehicles()
assert (123.456, 34.5678) == location.location
assert 121 == location.heading
assert location.location == (123.456, 34.5678)
assert location.heading == 121


@pytest.mark.asyncio
Expand Down Expand Up @@ -364,8 +364,8 @@ async def test_get_remote_position_too_old(bmw_fixture: respx.Router):

await vehicle.remote_services.trigger_remote_vehicle_finder()

assert (48.177334, 11.556274) == location.location
assert 180 == location.heading
assert location.location == (48.177334, 11.556274)
assert location.heading == 180


@pytest.mark.asyncio
Expand Down
8 changes: 4 additions & 4 deletions bimmer_connected/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
async def test_drive_train(bmw_fixture: respx.Router):
"""Tests available attribute."""
vehicle = (await prepare_account_with_vehicles()).get_vehicle(VIN_G26)
assert [
assert get_class_property_names(vehicle) == [
"available_attributes",
"brand",
"drive_train",
Expand Down Expand Up @@ -51,7 +51,7 @@ async def test_drive_train(bmw_fixture: respx.Router):
"name",
"timestamp",
"vin",
] == get_class_property_names(vehicle)
]


def test_parse_datetime(caplog):
Expand Down Expand Up @@ -89,10 +89,10 @@ def test_json_encoder():
cls=MyBMWJSONEncoder,
)

assert (
assert encoded == (
'{"datetime": "2022-06-02T22:19:34.123456", "date": "2022-06-02", "value": [17, "mi"],'
' "list": [{"value_int": 1, "value_str": "string"}, "America/Los_Angeles"]}'
) == encoded
)


def test_charging_settings():
Expand Down
22 changes: 11 additions & 11 deletions bimmer_connected/tests/test_vehicle.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@ async def test_drive_train(caplog, bmw_fixture: respx.Router):
"""Tests around drive_train attribute."""
account = await prepare_account_with_vehicles()
vehicle = account.get_vehicle(VIN_F31)
assert DriveTrainType.COMBUSTION == vehicle.drive_train
assert vehicle.drive_train == DriveTrainType.COMBUSTION

vehicle = account.get_vehicle(VIN_G01)
assert DriveTrainType.PLUGIN_HYBRID == vehicle.drive_train
assert vehicle.drive_train == DriveTrainType.PLUGIN_HYBRID

vehicle = account.get_vehicle(VIN_G26)
assert DriveTrainType.ELECTRIC == vehicle.drive_train
assert vehicle.drive_train == DriveTrainType.ELECTRIC

vehicle = account.get_vehicle(VIN_I01_NOREX)
assert DriveTrainType.ELECTRIC == vehicle.drive_train
assert vehicle.drive_train == DriveTrainType.ELECTRIC

vehicle = account.get_vehicle(VIN_I01_REX)
assert DriveTrainType.ELECTRIC_WITH_RANGE_EXTENDER == vehicle.drive_train
assert vehicle.drive_train == DriveTrainType.ELECTRIC_WITH_RANGE_EXTENDER

assert len(get_deprecation_warning_count(caplog)) == 0

Expand Down Expand Up @@ -145,10 +145,10 @@ async def test_available_attributes(caplog, bmw_fixture: respx.Router):
account = await prepare_account_with_vehicles()

vehicle = account.get_vehicle(VIN_F31)
assert ["gps_position", "vin"] == vehicle.available_attributes
assert vehicle.available_attributes == ["gps_position", "vin"]

vehicle = account.get_vehicle(VIN_G01)
assert [
assert vehicle.available_attributes == [
"gps_position",
"vin",
"remaining_range_total",
Expand Down Expand Up @@ -176,10 +176,10 @@ async def test_available_attributes(caplog, bmw_fixture: respx.Router):
"timestamp",
"lids",
"windows",
] == vehicle.available_attributes
]

vehicle = account.get_vehicle(VIN_G26)
assert [
assert vehicle.available_attributes == [
"gps_position",
"vin",
"remaining_range_total",
Expand All @@ -204,7 +204,7 @@ async def test_available_attributes(caplog, bmw_fixture: respx.Router):
"timestamp",
"lids",
"windows",
] == vehicle.available_attributes
]

assert len(get_deprecation_warning_count(caplog)) == 0

Expand All @@ -219,7 +219,7 @@ async def test_vehicle_image(caplog, bmw_fixture: respx.Router):
params={"carView": "FrontView"},
headers={"accept": "image/png", "bmw-app-vehicle-type": "connected", "bmw-vin": VIN_G01},
).respond(200, content="png_image")
assert b"png_image" == await vehicle.get_vehicle_image(VehicleViewDirection.FRONT)
assert await vehicle.get_vehicle_image(VehicleViewDirection.FRONT) == b"png_image"

assert len(get_deprecation_warning_count(caplog)) == 0

Expand Down
Loading

0 comments on commit 812aa5a

Please sign in to comment.