Update python to 3.12 (#4815)
* Update python to 3.12 * Fix tests and deprecations * Fix other references to 3.11 * build.json doesn't exist
This commit is contained in:
parent
2a29b801a4
commit
2da27937a5
|
@ -29,7 +29,7 @@
|
|||
"files.trimTrailingWhitespace": true,
|
||||
"python.pythonPath": "/usr/local/bin/python3",
|
||||
"python.formatting.provider": "black",
|
||||
"python.formatting.blackArgs": ["--target-version", "py311"],
|
||||
"python.formatting.blackArgs": ["--target-version", "py312"],
|
||||
"python.formatting.blackPath": "/usr/local/bin/black"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ on:
|
|||
- setup.py
|
||||
|
||||
env:
|
||||
DEFAULT_PYTHON: "3.11"
|
||||
DEFAULT_PYTHON: "3.12"
|
||||
BUILD_NAME: supervisor
|
||||
BUILD_TYPE: supervisor
|
||||
|
||||
|
@ -75,7 +75,7 @@ jobs:
|
|||
- name: Check if requirements files changed
|
||||
id: requirements
|
||||
run: |
|
||||
if [[ "${{ steps.changed_files.outputs.all }}" =~ (requirements.txt|build.json) ]]; then
|
||||
if [[ "${{ steps.changed_files.outputs.all }}" =~ (requirements.txt|build.yaml) ]]; then
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
|
@ -108,7 +108,7 @@ jobs:
|
|||
if: needs.init.outputs.requirements == 'true'
|
||||
uses: home-assistant/wheels@2024.01.0
|
||||
with:
|
||||
abi: cp311
|
||||
abi: cp312
|
||||
tag: musllinux_1_2
|
||||
arch: ${{ matrix.arch }}
|
||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||
|
|
|
@ -8,7 +8,7 @@ on:
|
|||
pull_request: ~
|
||||
|
||||
env:
|
||||
DEFAULT_PYTHON: "3.11"
|
||||
DEFAULT_PYTHON: "3.12"
|
||||
PRE_COMMIT_CACHE: ~/.cache/pre-commit
|
||||
|
||||
concurrency:
|
||||
|
@ -88,7 +88,7 @@ jobs:
|
|||
- name: Run black
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
black --target-version py311 --check supervisor tests setup.py
|
||||
black --target-version py312 --check supervisor tests setup.py
|
||||
|
||||
lint-dockerfile:
|
||||
name: Check Dockerfile
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.1.0
|
||||
rev: 23.12.1
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
- --safe
|
||||
- --quiet
|
||||
- --target-version
|
||||
- py311
|
||||
- py312
|
||||
files: ^((supervisor|tests)/.+)?[^/]+\.py$
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
rev: 7.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies:
|
||||
|
@ -18,17 +18,17 @@ repos:
|
|||
- pydocstyle==6.3.0
|
||||
files: ^(supervisor|script|tests)/.+\.py$
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.3.0
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: check-executables-have-shebangs
|
||||
stages: [manual]
|
||||
- id: check-json
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
rev: 5.13.2
|
||||
hooks:
|
||||
- id: isort
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.15.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py311-plus]
|
||||
args: [--py312-plus]
|
||||
|
|
10
build.yaml
10
build.yaml
|
@ -1,10 +1,10 @@
|
|||
image: ghcr.io/home-assistant/{arch}-hassio-supervisor
|
||||
build_from:
|
||||
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.11-alpine3.18
|
||||
armhf: ghcr.io/home-assistant/armhf-base-python:3.11-alpine3.18
|
||||
armv7: ghcr.io/home-assistant/armv7-base-python:3.11-alpine3.18
|
||||
amd64: ghcr.io/home-assistant/amd64-base-python:3.11-alpine3.18
|
||||
i386: ghcr.io/home-assistant/i386-base-python:3.11-alpine3.18
|
||||
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.12-alpine3.18
|
||||
armhf: ghcr.io/home-assistant/armhf-base-python:3.12-alpine3.18
|
||||
armv7: ghcr.io/home-assistant/armv7-base-python:3.12-alpine3.18
|
||||
amd64: ghcr.io/home-assistant/amd64-base-python:3.12-alpine3.18
|
||||
i386: ghcr.io/home-assistant/i386-base-python:3.12-alpine3.18
|
||||
codenotary:
|
||||
signer: notary@home-assistant.io
|
||||
base_image: notary@home-assistant.io
|
||||
|
|
|
@ -12,7 +12,7 @@ authors = [
|
|||
{ name = "The Home Assistant Authors", email = "hello@home-assistant.io" },
|
||||
]
|
||||
keywords = ["docker", "home-assistant", "api"]
|
||||
requires-python = ">=3.11.0"
|
||||
requires-python = ">=3.12.0"
|
||||
|
||||
[project.urls]
|
||||
"Homepage" = "https://www.home-assistant.io/"
|
||||
|
|
|
@ -6,7 +6,7 @@ pre-commit==3.6.0
|
|||
pydocstyle==6.3.0
|
||||
pylint==3.0.3
|
||||
pytest-aiohttp==1.0.5
|
||||
pytest-asyncio==0.18.3
|
||||
pytest-asyncio==0.23.3
|
||||
pytest-cov==4.1.0
|
||||
pytest-timeout==2.2.0
|
||||
pytest==7.4.4
|
||||
|
|
|
@ -1155,7 +1155,11 @@ class Addon(AddonModel):
|
|||
def _extract_tarfile():
|
||||
"""Extract tar backup."""
|
||||
with tar_file as backup:
|
||||
backup.extractall(path=Path(temp), members=secure_path(backup))
|
||||
backup.extractall(
|
||||
path=Path(temp),
|
||||
members=secure_path(backup),
|
||||
filter="fully_trusted",
|
||||
)
|
||||
|
||||
try:
|
||||
await self.sys_run_in_executor(_extract_tarfile)
|
||||
|
|
|
@ -315,7 +315,11 @@ class Backup(CoreSysAttributes):
|
|||
def _extract_backup():
|
||||
"""Extract a backup."""
|
||||
with tarfile.open(self.tarfile, "r:") as tar:
|
||||
tar.extractall(path=self._tmp.name, members=secure_path(tar))
|
||||
tar.extractall(
|
||||
path=self._tmp.name,
|
||||
members=secure_path(tar),
|
||||
filter="fully_trusted",
|
||||
)
|
||||
|
||||
await self.sys_run_in_executor(_extract_backup)
|
||||
|
||||
|
@ -535,7 +539,9 @@ class Backup(CoreSysAttributes):
|
|||
gzip=self.compressed,
|
||||
bufsize=BUF_SIZE,
|
||||
) as tar_file:
|
||||
tar_file.extractall(path=origin_dir, members=tar_file)
|
||||
tar_file.extractall(
|
||||
path=origin_dir, members=tar_file, filter="fully_trusted"
|
||||
)
|
||||
_LOGGER.info("Restore folder %s done", name)
|
||||
except (tarfile.TarError, OSError) as err:
|
||||
_LOGGER.warning("Can't restore folder %s: %s", name, err)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""Bootstrap Supervisor."""
|
||||
from datetime import datetime
|
||||
from datetime import UTC, datetime
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path, PurePath
|
||||
|
@ -50,7 +50,7 @@ MOUNTS_CREDENTIALS = PurePath(".mounts_credentials")
|
|||
EMERGENCY_DATA = PurePath("emergency")
|
||||
ADDON_CONFIGS = PurePath("addon_configs")
|
||||
|
||||
DEFAULT_BOOT_TIME = datetime.utcfromtimestamp(0).isoformat()
|
||||
DEFAULT_BOOT_TIME = datetime.fromtimestamp(0, UTC).isoformat()
|
||||
|
||||
# We filter out UTC because it's the system default fallback
|
||||
# Core also not respect the cotnainer timezone and reset timezones
|
||||
|
@ -164,7 +164,7 @@ class CoreConfig(FileConfiguration):
|
|||
|
||||
boot_time = parse_datetime(boot_str)
|
||||
if not boot_time:
|
||||
return datetime.utcfromtimestamp(1)
|
||||
return datetime.fromtimestamp(1, UTC)
|
||||
return boot_time
|
||||
|
||||
@last_boot.setter
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""Read hardware info from system."""
|
||||
from datetime import datetime
|
||||
from datetime import UTC, datetime
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
@ -55,7 +55,7 @@ class HwHelper(CoreSysAttributes):
|
|||
_LOGGER.error("Can't found last boot time!")
|
||||
return None
|
||||
|
||||
return datetime.utcfromtimestamp(int(found.group(1)))
|
||||
return datetime.fromtimestamp(int(found.group(1)), UTC)
|
||||
|
||||
def hide_virtual_device(self, udev_device: pyudev.Device) -> bool:
|
||||
"""Small helper to hide not needed Devices."""
|
||||
|
|
|
@ -411,7 +411,11 @@ class HomeAssistant(FileConfiguration, CoreSysAttributes):
|
|||
def _extract_tarfile():
|
||||
"""Extract tar backup."""
|
||||
with tar_file as backup:
|
||||
backup.extractall(path=temp_path, members=secure_path(backup))
|
||||
backup.extractall(
|
||||
path=temp_path,
|
||||
members=secure_path(backup),
|
||||
filter="fully_trusted",
|
||||
)
|
||||
|
||||
try:
|
||||
await self.sys_run_in_executor(_extract_tarfile)
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
"""Tools file for Supervisor."""
|
||||
from contextlib import suppress
|
||||
from datetime import datetime, timedelta, timezone, tzinfo
|
||||
from datetime import UTC, datetime, timedelta, timezone, tzinfo
|
||||
import re
|
||||
from typing import Any
|
||||
import zoneinfo
|
||||
|
||||
import ciso8601
|
||||
|
||||
UTC = timezone.utc
|
||||
|
||||
|
||||
# Copyright (c) Django Software Foundation and individual contributors.
|
||||
# All rights reserved.
|
||||
# https://github.com/django/django/blob/master/LICENSE
|
||||
|
@ -67,7 +64,7 @@ def utcnow() -> datetime:
|
|||
|
||||
def utc_from_timestamp(timestamp: float) -> datetime:
|
||||
"""Return a UTC time from a timestamp."""
|
||||
return datetime.utcfromtimestamp(timestamp).replace(tzinfo=UTC)
|
||||
return datetime.fromtimestamp(timestamp, UTC).replace(tzinfo=UTC)
|
||||
|
||||
|
||||
def get_time_zone(time_zone_str: str) -> tzinfo | None:
|
||||
|
|
|
@ -110,7 +110,6 @@ async def test_bad_requests(
|
|||
fail_on_query_string,
|
||||
api_system,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
event_loop: asyncio.BaseEventLoop,
|
||||
) -> None:
|
||||
"""Test request paths that should be filtered."""
|
||||
|
||||
|
@ -122,7 +121,7 @@ async def test_bad_requests(
|
|||
man_params = ""
|
||||
|
||||
http = urllib3.PoolManager()
|
||||
resp = await event_loop.run_in_executor(
|
||||
resp = await asyncio.get_running_loop().run_in_executor(
|
||||
None,
|
||||
http.request,
|
||||
"GET",
|
||||
|
|
|
@ -293,7 +293,6 @@ async def fixture_all_dbus_services(
|
|||
|
||||
@pytest.fixture
|
||||
async def coresys(
|
||||
event_loop,
|
||||
docker,
|
||||
dbus_session_bus,
|
||||
all_dbus_services,
|
||||
|
@ -590,7 +589,7 @@ async def backups(
|
|||
) -> list[Backup]:
|
||||
"""Create and return mock backups."""
|
||||
for i in range(request.param if hasattr(request, "param") else 5):
|
||||
slug = f"sn{i+1}"
|
||||
slug = f"sn{i + 1}"
|
||||
temp_tar = Path(tmp_path, f"{slug}.tar")
|
||||
with SecureTarFile(temp_tar, "w"):
|
||||
pass
|
||||
|
|
|
@ -274,9 +274,7 @@ async def test_exception_conditions(coresys: CoreSys):
|
|||
await test.execute()
|
||||
|
||||
|
||||
async def test_execution_limit_single_wait(
|
||||
coresys: CoreSys, event_loop: asyncio.BaseEventLoop
|
||||
):
|
||||
async def test_execution_limit_single_wait(coresys: CoreSys):
|
||||
"""Test the single wait job execution limit."""
|
||||
|
||||
class TestClass:
|
||||
|
@ -302,9 +300,7 @@ async def test_execution_limit_single_wait(
|
|||
await asyncio.gather(*[test.execute(0.1), test.execute(0.1), test.execute(0.1)])
|
||||
|
||||
|
||||
async def test_execution_limit_throttle_wait(
|
||||
coresys: CoreSys, event_loop: asyncio.BaseEventLoop
|
||||
):
|
||||
async def test_execution_limit_throttle_wait(coresys: CoreSys):
|
||||
"""Test the throttle wait job execution limit."""
|
||||
|
||||
class TestClass:
|
||||
|
@ -339,7 +335,7 @@ async def test_execution_limit_throttle_wait(
|
|||
|
||||
@pytest.mark.parametrize("error", [None, PluginJobError])
|
||||
async def test_execution_limit_throttle_rate_limit(
|
||||
coresys: CoreSys, event_loop: asyncio.BaseEventLoop, error: JobException | None
|
||||
coresys: CoreSys, error: JobException | None
|
||||
):
|
||||
"""Test the throttle wait job execution limit."""
|
||||
|
||||
|
@ -379,9 +375,7 @@ async def test_execution_limit_throttle_rate_limit(
|
|||
assert test.call == 3
|
||||
|
||||
|
||||
async def test_execution_limit_throttle(
|
||||
coresys: CoreSys, event_loop: asyncio.BaseEventLoop
|
||||
):
|
||||
async def test_execution_limit_throttle(coresys: CoreSys):
|
||||
"""Test the ignore conditions decorator."""
|
||||
|
||||
class TestClass:
|
||||
|
@ -414,9 +408,7 @@ async def test_execution_limit_throttle(
|
|||
assert test.call == 1
|
||||
|
||||
|
||||
async def test_execution_limit_once(
|
||||
coresys: CoreSys, event_loop: asyncio.BaseEventLoop
|
||||
):
|
||||
async def test_execution_limit_once(coresys: CoreSys):
|
||||
"""Test the ignore conditions decorator."""
|
||||
|
||||
class TestClass:
|
||||
|
@ -439,7 +431,7 @@ async def test_execution_limit_once(
|
|||
await asyncio.sleep(sleep)
|
||||
|
||||
test = TestClass(coresys)
|
||||
run_task = event_loop.create_task(test.execute(0.3))
|
||||
run_task = asyncio.get_running_loop().create_task(test.execute(0.3))
|
||||
|
||||
await asyncio.sleep(0.1)
|
||||
with pytest.raises(JobException):
|
||||
|
@ -595,7 +587,7 @@ async def test_host_network(coresys: CoreSys):
|
|||
assert await test.execute()
|
||||
|
||||
|
||||
async def test_job_group_once(coresys: CoreSys, event_loop: asyncio.BaseEventLoop):
|
||||
async def test_job_group_once(coresys: CoreSys):
|
||||
"""Test job group once execution limitation."""
|
||||
|
||||
class TestClass(JobGroup):
|
||||
|
@ -644,7 +636,7 @@ async def test_job_group_once(coresys: CoreSys, event_loop: asyncio.BaseEventLoo
|
|||
return True
|
||||
|
||||
test = TestClass(coresys)
|
||||
run_task = event_loop.create_task(test.execute())
|
||||
run_task = asyncio.get_running_loop().create_task(test.execute())
|
||||
await asyncio.sleep(0)
|
||||
|
||||
# All methods with group limits should be locked
|
||||
|
@ -664,7 +656,7 @@ async def test_job_group_once(coresys: CoreSys, event_loop: asyncio.BaseEventLoo
|
|||
assert await run_task
|
||||
|
||||
|
||||
async def test_job_group_wait(coresys: CoreSys, event_loop: asyncio.BaseEventLoop):
|
||||
async def test_job_group_wait(coresys: CoreSys):
|
||||
"""Test job group wait execution limitation."""
|
||||
|
||||
class TestClass(JobGroup):
|
||||
|
@ -706,6 +698,7 @@ async def test_job_group_wait(coresys: CoreSys, event_loop: asyncio.BaseEventLoo
|
|||
self.other_count += 1
|
||||
|
||||
test = TestClass(coresys)
|
||||
event_loop = asyncio.get_running_loop()
|
||||
run_task = event_loop.create_task(test.execute())
|
||||
await asyncio.sleep(0)
|
||||
|
||||
|
@ -725,7 +718,7 @@ async def test_job_group_wait(coresys: CoreSys, event_loop: asyncio.BaseEventLoo
|
|||
assert test.other_count == 1
|
||||
|
||||
|
||||
async def test_job_cleanup(coresys: CoreSys, event_loop: asyncio.BaseEventLoop):
|
||||
async def test_job_cleanup(coresys: CoreSys):
|
||||
"""Test job is cleaned up."""
|
||||
|
||||
class TestClass:
|
||||
|
@ -745,7 +738,7 @@ async def test_job_cleanup(coresys: CoreSys, event_loop: asyncio.BaseEventLoop):
|
|||
return True
|
||||
|
||||
test = TestClass(coresys)
|
||||
run_task = event_loop.create_task(test.execute())
|
||||
run_task = asyncio.get_running_loop().create_task(test.execute())
|
||||
await asyncio.sleep(0)
|
||||
|
||||
assert coresys.jobs.jobs == [test.job]
|
||||
|
@ -758,7 +751,7 @@ async def test_job_cleanup(coresys: CoreSys, event_loop: asyncio.BaseEventLoop):
|
|||
assert test.job.done
|
||||
|
||||
|
||||
async def test_job_skip_cleanup(coresys: CoreSys, event_loop: asyncio.BaseEventLoop):
|
||||
async def test_job_skip_cleanup(coresys: CoreSys):
|
||||
"""Test job is left in job manager when cleanup is false."""
|
||||
|
||||
class TestClass:
|
||||
|
@ -782,7 +775,7 @@ async def test_job_skip_cleanup(coresys: CoreSys, event_loop: asyncio.BaseEventL
|
|||
return True
|
||||
|
||||
test = TestClass(coresys)
|
||||
run_task = event_loop.create_task(test.execute())
|
||||
run_task = asyncio.get_running_loop().create_task(test.execute())
|
||||
await asyncio.sleep(0)
|
||||
|
||||
assert coresys.jobs.jobs == [test.job]
|
||||
|
@ -795,9 +788,7 @@ async def test_job_skip_cleanup(coresys: CoreSys, event_loop: asyncio.BaseEventL
|
|||
assert test.job.done
|
||||
|
||||
|
||||
async def test_execution_limit_group_throttle(
|
||||
coresys: CoreSys, event_loop: asyncio.BaseEventLoop
|
||||
):
|
||||
async def test_execution_limit_group_throttle(coresys: CoreSys):
|
||||
"""Test the group throttle execution limit."""
|
||||
|
||||
class TestClass(JobGroup):
|
||||
|
@ -844,9 +835,7 @@ async def test_execution_limit_group_throttle(
|
|||
assert test2.call == 2
|
||||
|
||||
|
||||
async def test_execution_limit_group_throttle_wait(
|
||||
coresys: CoreSys, event_loop: asyncio.BaseEventLoop
|
||||
):
|
||||
async def test_execution_limit_group_throttle_wait(coresys: CoreSys):
|
||||
"""Test the group throttle wait job execution limit."""
|
||||
|
||||
class TestClass(JobGroup):
|
||||
|
@ -897,7 +886,7 @@ async def test_execution_limit_group_throttle_wait(
|
|||
|
||||
@pytest.mark.parametrize("error", [None, PluginJobError])
|
||||
async def test_execution_limit_group_throttle_rate_limit(
|
||||
coresys: CoreSys, event_loop: asyncio.BaseEventLoop, error: JobException | None
|
||||
coresys: CoreSys, error: JobException | None
|
||||
):
|
||||
"""Test the group throttle rate limit job execution limit."""
|
||||
|
||||
|
|
Loading…
Reference in New Issue