Merge pull request 'Qubasa-hsjobeki/bump-nixpkgs' (#4205) from Qubasa-hsjobeki/bump-nixpkgs into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4205
This commit is contained in:
6
flake.lock
generated
6
flake.lock
generated
@@ -164,10 +164,10 @@
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 315532800,
|
||||
"narHash": "sha256-VgDAFPxHNhCfC7rI5I5wFqdiVJBH43zUefVo8hwo7cI=",
|
||||
"rev": "41da1e3ea8e23e094e5e3eeb1e6b830468a7399e",
|
||||
"narHash": "sha256-0HRxGUoOMtOYnwlMWY0AkuU88WHaI3Q5GEILmsWpI8U=",
|
||||
"rev": "a48741b083d4f36dd79abd9f760c84da6b4dc0e5",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre814815.41da1e3ea8e2/nixexprs.tar.xz"
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre823094.a48741b083d4/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
|
||||
@@ -15,7 +15,7 @@ find = {}
|
||||
[tool.setuptools.package-data]
|
||||
test_driver = ["py.typed"]
|
||||
[tool.mypy]
|
||||
python_version = "3.12"
|
||||
python_version = "3.13"
|
||||
warn_redundant_casts = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_defs = true
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# ruff: noqa: N801
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "4.0")
|
||||
|
||||
@@ -30,7 +30,7 @@ norecursedirs = "tests/helpers"
|
||||
markers = ["impure"]
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.12"
|
||||
python_version = "3.13"
|
||||
warn_redundant_casts = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_defs = true
|
||||
|
||||
@@ -7,7 +7,7 @@ import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def wayland_compositor() -> Generator[Popen, None, None]:
|
||||
def wayland_compositor() -> Generator[Popen]:
|
||||
# Start the Wayland compositor (e.g., Weston)
|
||||
# compositor = Popen(["weston", "--backend=headless-backend.so"])
|
||||
compositor = Popen(["weston"])
|
||||
@@ -20,7 +20,7 @@ GtkProc = NewType("GtkProc", Popen)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app() -> Generator[GtkProc, None, None]:
|
||||
def app() -> Generator[GtkProc]:
|
||||
cmd = [sys.executable, "-m", "clan_app"]
|
||||
print(f"Running: {cmd}")
|
||||
rapp = Popen(
|
||||
|
||||
@@ -5,9 +5,9 @@ import shutil
|
||||
import subprocess as sp
|
||||
import tempfile
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable, Iterator
|
||||
from collections.abc import Iterator
|
||||
from pathlib import Path
|
||||
from typing import Any, NamedTuple
|
||||
from typing import NamedTuple
|
||||
|
||||
import pytest
|
||||
from clan_cli.tests import age_keys
|
||||
@@ -38,7 +38,12 @@ def def_value() -> defaultdict:
|
||||
return defaultdict(def_value)
|
||||
|
||||
|
||||
nested_dict: Callable[[], dict[str, Any]] = lambda: defaultdict(def_value)
|
||||
def nested_dict() -> defaultdict:
|
||||
"""
|
||||
Creates a defaultdict that allows for arbitrary levels of nesting.
|
||||
For example: d['a']['b']['c'] = value
|
||||
"""
|
||||
return defaultdict(def_value)
|
||||
|
||||
|
||||
# Substitutes strings in a file.
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
|
||||
def def_value() -> defaultdict:
|
||||
return defaultdict(def_value)
|
||||
|
||||
|
||||
# allows defining nested dictionary in a single line
|
||||
nested_dict: Callable[[], dict[str, Any]] = lambda: defaultdict(def_value)
|
||||
@@ -4,14 +4,13 @@ import logging
|
||||
from clan_cli.completions import add_dynamic_completer, complete_machines
|
||||
from clan_lib.errors import ClanError
|
||||
from clan_lib.machines.machines import Machine
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .generate import Var
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VarStatus:
|
||||
def __init__(
|
||||
|
||||
@@ -16,14 +16,14 @@ from typing import (
|
||||
)
|
||||
|
||||
from clan_lib.api.util import JSchemaTypeError
|
||||
from clan_lib.errors import ClanError
|
||||
from .serde import dataclass_to_dict, from_dict, sanitize_string
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
from .serde import dataclass_to_dict, from_dict, sanitize_string
|
||||
|
||||
__all__ = ["dataclass_to_dict", "from_dict", "sanitize_string"]
|
||||
|
||||
from clan_lib.errors import ClanError
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@@ -146,8 +146,31 @@ def is_union_type(type_hint: type | UnionType) -> bool:
|
||||
|
||||
|
||||
def is_type_in_union(union_type: type | UnionType, target_type: type) -> bool:
|
||||
if get_origin(union_type) is UnionType:
|
||||
return any(issubclass(arg, target_type) for arg in get_args(union_type))
|
||||
# Check for Union from typing module (Union[str, None]) or UnionType (str | None)
|
||||
if get_origin(union_type) in (Union, UnionType):
|
||||
args = get_args(union_type)
|
||||
for arg in args:
|
||||
# Handle None type specially since it's not a class
|
||||
if arg is None or arg is type(None):
|
||||
if target_type is type(None):
|
||||
return True
|
||||
# For generic types like dict[str, str], check their origin
|
||||
elif get_origin(arg) is not None:
|
||||
if get_origin(arg) == target_type:
|
||||
return True
|
||||
# Also check if target_type is a generic with same origin
|
||||
elif get_origin(target_type) is not None and get_origin(
|
||||
arg
|
||||
) == get_origin(target_type):
|
||||
return True
|
||||
# For actual classes, use issubclass
|
||||
elif inspect.isclass(arg) and inspect.isclass(target_type):
|
||||
if issubclass(arg, target_type):
|
||||
return True
|
||||
# For non-class types, use direct comparison
|
||||
elif arg == target_type:
|
||||
return True
|
||||
return False
|
||||
return union_type == target_type
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import pytest
|
||||
from clan_lib.api import dataclass_to_dict, from_dict
|
||||
from clan_lib.errors import ClanError
|
||||
from clan_lib.machines import machines
|
||||
from clan_lib.api.serde import is_type_in_union
|
||||
|
||||
|
||||
def test_simple() -> None:
|
||||
@@ -216,6 +217,44 @@ def test_none_or_string() -> None:
|
||||
assert checked3 is None
|
||||
|
||||
|
||||
def test_union_with_none_edge_cases() -> None:
|
||||
"""
|
||||
Test various union types with None to ensure issubclass() error is avoided.
|
||||
This specifically tests the fix for the TypeError in is_type_in_union.
|
||||
"""
|
||||
# Test basic types with None
|
||||
assert from_dict(str | None, None) is None
|
||||
assert from_dict(str | None, "hello") == "hello"
|
||||
|
||||
# Test dict with None - this was the specific case that failed
|
||||
assert from_dict(dict[str, str] | None, None) is None
|
||||
assert from_dict(dict[str, str] | None, {"key": "value"}) == {"key": "value"}
|
||||
|
||||
# Test list with None
|
||||
assert from_dict(list[str] | None, None) is None
|
||||
assert from_dict(list[str] | None, ["a", "b"]) == ["a", "b"]
|
||||
|
||||
# Test dataclass with None
|
||||
@dataclass
|
||||
class TestClass:
|
||||
value: str
|
||||
|
||||
assert from_dict(TestClass | None, None) is None
|
||||
assert from_dict(TestClass | None, {"value": "test"}) == TestClass(value="test")
|
||||
|
||||
# Test Path with None (since it's used in the original failing test)
|
||||
assert from_dict(Path | None, None) is None
|
||||
assert from_dict(Path | None, "/home/test") == Path("/home/test")
|
||||
|
||||
# Test that the is_type_in_union function works correctly
|
||||
# This is the core of what was fixed - ensuring None doesn't cause issubclass error
|
||||
# These should not raise TypeError anymore
|
||||
assert is_type_in_union(str | None, type(None)) is True
|
||||
assert is_type_in_union(dict[str, str] | None, type(None)) is True
|
||||
assert is_type_in_union(list[str] | None, type(None)) is True
|
||||
assert is_type_in_union(Path | None, type(None)) is True
|
||||
|
||||
|
||||
def test_roundtrip_escape() -> None:
|
||||
assert from_dict(str, "\n") == "\n"
|
||||
assert dataclass_to_dict("\n") == "\n"
|
||||
|
||||
@@ -179,6 +179,7 @@ def terminate_process_group(process: subprocess.Popen) -> Iterator[None]:
|
||||
try:
|
||||
process_group = os.getpgid(process.pid)
|
||||
except ProcessLookupError:
|
||||
yield
|
||||
return
|
||||
if process_group == os.getpgid(os.getpid()):
|
||||
msg = "Bug! Refusing to terminate the current process group"
|
||||
|
||||
@@ -42,7 +42,10 @@ def delete_machine(machine: Machine) -> None:
|
||||
|
||||
# louis@(2025-02-04): clean-up legacy (pre-vars) secrets:
|
||||
sops_folder = sops_secrets_folder(machine.flake.path)
|
||||
filter_fn = lambda secret_name: secret_name.startswith(f"{machine.name}-")
|
||||
|
||||
def filter_fn(secret_name: str) -> bool:
|
||||
return secret_name.startswith(f"{machine.name}-")
|
||||
|
||||
for secret_name in list_secrets(machine.flake.path, filter_fn):
|
||||
secret_path = sops_folder / secret_name
|
||||
changed_paths.append(secret_path)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# ruff: noqa: SLF001
|
||||
import ipaddress
|
||||
import logging
|
||||
import os
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import argparse
|
||||
import sys
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
@@ -118,9 +119,6 @@ def epilog_to_md(text: str) -> str:
|
||||
return md
|
||||
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def contains_https_link(line: str) -> bool:
|
||||
pattern = r"https://\S+"
|
||||
return re.search(pattern, line) is not None
|
||||
|
||||
@@ -49,9 +49,12 @@ filterwarnings = "default::ResourceWarning"
|
||||
python_files = ["test_*.py", "*_test.py"]
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.12"
|
||||
python_version = "3.13"
|
||||
warn_redundant_casts = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_defs = true
|
||||
no_implicit_optional = true
|
||||
exclude = "clan_lib.nixpkgs"
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py313"
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from collections.abc import Callable
|
||||
from typing import Any, Generic, TypeVar
|
||||
from typing import Any, TypeVar
|
||||
|
||||
import gi
|
||||
|
||||
@@ -22,7 +22,7 @@ V = TypeVar(
|
||||
# clan_vm_manager/components/gkvstore.py:21: error: Definition of "newv" in base class "Object" is incompatible with definition in base class "GInterface" [misc]
|
||||
# clan_vm_manager/components/gkvstore.py:21: error: Definition of "install_properties" in base class "Object" is incompatible with definition in base class "GInterface" [misc]
|
||||
# clan_vm_manager/components/gkvstore.py:21: error: Definition of "getv" in base class "Object" is incompatible with definition in base class "GInterface" [misc]
|
||||
class GKVStore(GObject.GObject, Gio.ListModel, Generic[K, V]): # type: ignore[misc]
|
||||
class GKVStore[K, V: GObject.Object](GObject.GObject, Gio.ListModel): # type: ignore[misc]
|
||||
"""
|
||||
A simple key-value store that implements the Gio.ListModel interface, with generic types for keys and values.
|
||||
Only use self[key] and del self[key] for accessing the items for better performance.
|
||||
|
||||
@@ -143,7 +143,7 @@ class VMObject(GObject.Object):
|
||||
# We use a context manager to create the machine object
|
||||
# and make sure it is destroyed when the context is exited
|
||||
@contextmanager
|
||||
def _create_machine(self) -> Generator[Machine, None, None]:
|
||||
def _create_machine(self) -> Generator[Machine]:
|
||||
uri = ClanURI.from_str(
|
||||
url=str(self.data.flake.flake_url), machine_name=self.data.flake.flake_attr
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ from gi.repository import Adw, Gio, GObject, Gtk
|
||||
ListItem = TypeVar("ListItem", bound=GObject.Object)
|
||||
|
||||
|
||||
def create_details_list(
|
||||
def create_details_list[ListItem: GObject.Object](
|
||||
model: Gio.ListStore, render_row: Callable[[Gtk.ListBox, ListItem], Gtk.Widget]
|
||||
) -> Gtk.ListBox:
|
||||
boxed_list = Gtk.ListBox()
|
||||
|
||||
@@ -32,7 +32,7 @@ ListItem = TypeVar("ListItem", bound=GObject.Object)
|
||||
CustomStore = TypeVar("CustomStore", bound=Gio.ListModel)
|
||||
|
||||
|
||||
def create_boxed_list(
|
||||
def create_boxed_list[CustomStore: Gio.ListModel, ListItem: GObject.Object](
|
||||
model: CustomStore,
|
||||
render_row: Callable[[Gtk.ListBox, ListItem], Gtk.Widget],
|
||||
) -> Gtk.ListBox:
|
||||
|
||||
@@ -30,7 +30,7 @@ norecursedirs = "tests/helpers"
|
||||
markers = ["impure"]
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.12"
|
||||
python_version = "3.13"
|
||||
warn_redundant_casts = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_defs = true
|
||||
|
||||
@@ -7,7 +7,7 @@ import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def wayland_compositor() -> Generator[Popen, None, None]:
|
||||
def wayland_compositor() -> Generator[Popen]:
|
||||
# Start the Wayland compositor (e.g., Weston)
|
||||
# compositor = Popen(["weston", "--backend=headless-backend.so"])
|
||||
compositor = Popen(["weston"])
|
||||
@@ -20,7 +20,7 @@ GtkProc = NewType("GtkProc", Popen)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app() -> Generator[GtkProc, None, None]:
|
||||
def app() -> Generator[GtkProc]:
|
||||
rapp = Popen([sys.executable, "-m", "clan_vm_manager"], text=True)
|
||||
yield GtkProc(rapp)
|
||||
# Cleanup: Terminate your application
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# ruff: noqa: RUF001
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
|
||||
@@ -26,7 +26,7 @@ addopts = "--durations 5 --color=yes --new-first" # Add --pdb for debugging
|
||||
norecursedirs = "tests/helpers"
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.12"
|
||||
python_version = "3.13"
|
||||
warn_redundant_casts = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_defs = true
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
{ zerotierone, lib }:
|
||||
{
|
||||
zerotierone,
|
||||
stdenv,
|
||||
lib,
|
||||
}:
|
||||
# halalify zerotierone
|
||||
zerotierone.overrideAttrs (_old: {
|
||||
meta = _old.meta // {
|
||||
zerotierone.overrideAttrs (old: {
|
||||
# Maybe a sandbox issue?
|
||||
# zerotierone> [phy] Binding UDP listen socket to 127.0.0.1/60002... FAILED.
|
||||
doCheck = old.doCheck && !stdenv.hostPlatform.isDarwin;
|
||||
meta = old.meta // {
|
||||
license = lib.licenses.apsl20;
|
||||
};
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[tool.mypy]
|
||||
python_version = "3.12"
|
||||
python_version = "3.13"
|
||||
pretty = true
|
||||
warn_redundant_casts = true
|
||||
disallow_untyped_calls = true
|
||||
@@ -8,7 +8,7 @@ no_implicit_optional = true
|
||||
exclude = "clan_cli.nixpkgs"
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py311"
|
||||
target-version = "py313"
|
||||
line-length = 88
|
||||
lint.select = [
|
||||
"A",
|
||||
|
||||
Reference in New Issue
Block a user