PLC0415: fix
This commit is contained in:
@@ -43,7 +43,7 @@ class ApiBridge(ABC):
|
|||||||
|
|
||||||
def process_request(self, request: BackendRequest) -> None:
|
def process_request(self, request: BackendRequest) -> None:
|
||||||
"""Process an API request through the middleware chain."""
|
"""Process an API request through the middleware chain."""
|
||||||
from .middleware import MiddlewareContext
|
from .middleware import MiddlewareContext # noqa: PLC0415
|
||||||
|
|
||||||
with ExitStack() as stack:
|
with ExitStack() as stack:
|
||||||
context = MiddlewareContext(
|
context = MiddlewareContext(
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from clan_app.api.middleware import (
|
|||||||
LoggingMiddleware,
|
LoggingMiddleware,
|
||||||
MethodExecutionMiddleware,
|
MethodExecutionMiddleware,
|
||||||
)
|
)
|
||||||
|
from clan_app.deps.http.http_server import HttpApiServer
|
||||||
from clan_app.deps.webview.webview import Size, SizeHint, Webview
|
from clan_app.deps.webview.webview import Size, SizeHint, Webview
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@@ -65,8 +66,6 @@ def app_run(app_opts: ClanAppOptions) -> int:
|
|||||||
# Start HTTP API server if requested
|
# Start HTTP API server if requested
|
||||||
http_server = None
|
http_server = None
|
||||||
if app_opts.http_api:
|
if app_opts.http_api:
|
||||||
from clan_app.deps.http.http_server import HttpApiServer
|
|
||||||
|
|
||||||
openapi_file = os.getenv("OPENAPI_FILE", None)
|
openapi_file = os.getenv("OPENAPI_FILE", None)
|
||||||
swagger_dist = os.getenv("SWAGGER_UI_DIST", None)
|
swagger_dist = os.getenv("SWAGGER_UI_DIST", None)
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ from clan_lib.api import MethodRegistry, message_queue
|
|||||||
from clan_lib.api.tasks import WebThread
|
from clan_lib.api.tasks import WebThread
|
||||||
|
|
||||||
from ._webview_ffi import _encode_c_string, _webview_lib
|
from ._webview_ffi import _encode_c_string, _webview_lib
|
||||||
|
from .webview_bridge import WebviewBridge
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from clan_app.api.middleware import Middleware
|
from clan_app.api.middleware import Middleware
|
||||||
|
|
||||||
from .webview_bridge import WebviewBridge
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ class Webview:
|
|||||||
shared_threads: dict[str, WebThread] | None = None
|
shared_threads: dict[str, WebThread] | None = None
|
||||||
|
|
||||||
# initialized later
|
# initialized later
|
||||||
_bridge: "WebviewBridge | None" = None
|
_bridge: WebviewBridge | None = None
|
||||||
_handle: Any | None = None
|
_handle: Any | None = None
|
||||||
_callbacks: dict[str, Callable[..., Any]] = field(default_factory=dict)
|
_callbacks: dict[str, Callable[..., Any]] = field(default_factory=dict)
|
||||||
_middleware: list["Middleware"] = field(default_factory=list)
|
_middleware: list["Middleware"] = field(default_factory=list)
|
||||||
@@ -132,10 +131,8 @@ class Webview:
|
|||||||
|
|
||||||
self._middleware.append(middleware)
|
self._middleware.append(middleware)
|
||||||
|
|
||||||
def create_bridge(self) -> "WebviewBridge":
|
def create_bridge(self) -> WebviewBridge:
|
||||||
"""Create and initialize the WebviewBridge with current middleware."""
|
"""Create and initialize the WebviewBridge with current middleware."""
|
||||||
from .webview_bridge import WebviewBridge
|
|
||||||
|
|
||||||
# Use shared_threads if provided, otherwise let WebviewBridge use its default
|
# Use shared_threads if provided, otherwise let WebviewBridge use its default
|
||||||
if self.shared_threads is not None:
|
if self.shared_threads is not None:
|
||||||
bridge = WebviewBridge(
|
bridge = WebviewBridge(
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ from clan_lib.api.tasks import WebThread
|
|||||||
|
|
||||||
from clan_app.api.api_bridge import ApiBridge, BackendRequest, BackendResponse
|
from clan_app.api.api_bridge import ApiBridge, BackendRequest, BackendResponse
|
||||||
|
|
||||||
from .webview import FuncStatus
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .webview import Webview
|
from .webview import Webview
|
||||||
|
|
||||||
@@ -32,6 +30,9 @@ class WebviewBridge(ApiBridge):
|
|||||||
)
|
)
|
||||||
|
|
||||||
log.debug(f"Sending response: {serialized}")
|
log.debug(f"Sending response: {serialized}")
|
||||||
|
# Import FuncStatus locally to avoid circular import
|
||||||
|
from .webview import FuncStatus # noqa: PLC0415
|
||||||
|
|
||||||
self.webview.return_(response._op_key, FuncStatus.SUCCESS, serialized) # noqa: SLF001
|
self.webview.return_(response._op_key, FuncStatus.SUCCESS, serialized) # noqa: SLF001
|
||||||
|
|
||||||
def handle_webview_call(
|
def handle_webview_call(
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from clan_cli.tests import fixtures_flakes
|
|||||||
from clan_cli.tests.age_keys import SopsSetup, assert_secrets_file_recipients
|
from clan_cli.tests.age_keys import SopsSetup, assert_secrets_file_recipients
|
||||||
from clan_cli.tests.helpers import cli
|
from clan_cli.tests.helpers import cli
|
||||||
from clan_cli.tests.stdout import CaptureOutput
|
from clan_cli.tests.stdout import CaptureOutput
|
||||||
|
from clan_lib.errors import ClanError
|
||||||
from clan_lib.flake import Flake
|
from clan_lib.flake import Flake
|
||||||
from clan_lib.persist.inventory_store import InventoryStore
|
from clan_lib.persist.inventory_store import InventoryStore
|
||||||
|
|
||||||
@@ -93,8 +94,6 @@ def test_machines_update_nonexistent_machine(
|
|||||||
test_flake_with_core: fixtures_flakes.FlakeForTest,
|
test_flake_with_core: fixtures_flakes.FlakeForTest,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that update command gives helpful error messages for non-existent machines."""
|
"""Test that update command gives helpful error messages for non-existent machines."""
|
||||||
from clan_lib.errors import ClanError
|
|
||||||
|
|
||||||
with pytest.raises(ClanError) as exc_info:
|
with pytest.raises(ClanError) as exc_info:
|
||||||
cli.run(
|
cli.run(
|
||||||
[
|
[
|
||||||
@@ -118,8 +117,6 @@ def test_machines_update_typo_in_machine_name(
|
|||||||
test_flake_with_core: fixtures_flakes.FlakeForTest,
|
test_flake_with_core: fixtures_flakes.FlakeForTest,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that update command suggests similar machine names for typos."""
|
"""Test that update command suggests similar machine names for typos."""
|
||||||
from clan_lib.errors import ClanError
|
|
||||||
|
|
||||||
with pytest.raises(ClanError) as exc_info:
|
with pytest.raises(ClanError) as exc_info:
|
||||||
cli.run(
|
cli.run(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
import shutil
|
||||||
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -11,6 +12,7 @@ from clan_cli.vars.check import check_vars
|
|||||||
from clan_cli.vars.generator import (
|
from clan_cli.vars.generator import (
|
||||||
Generator,
|
Generator,
|
||||||
GeneratorKey,
|
GeneratorKey,
|
||||||
|
dependencies_as_dir,
|
||||||
)
|
)
|
||||||
from clan_cli.vars.get import get_machine_var
|
from clan_cli.vars.get import get_machine_var
|
||||||
from clan_cli.vars.graph import all_missing_closure, requested_closure
|
from clan_cli.vars.graph import all_missing_closure, requested_closure
|
||||||
@@ -21,7 +23,7 @@ from clan_cli.vars.set import set_var
|
|||||||
from clan_lib.errors import ClanError
|
from clan_lib.errors import ClanError
|
||||||
from clan_lib.flake import Flake
|
from clan_lib.flake import Flake
|
||||||
from clan_lib.machines.machines import Machine
|
from clan_lib.machines.machines import Machine
|
||||||
from clan_lib.nix import nix_eval, run
|
from clan_lib.nix import nix_config, nix_eval, run
|
||||||
from clan_lib.vars.generate import (
|
from clan_lib.vars.generate import (
|
||||||
get_generators,
|
get_generators,
|
||||||
run_generators,
|
run_generators,
|
||||||
@@ -29,8 +31,6 @@ from clan_lib.vars.generate import (
|
|||||||
|
|
||||||
|
|
||||||
def test_dependencies_as_files(temp_dir: Path) -> None:
|
def test_dependencies_as_files(temp_dir: Path) -> None:
|
||||||
from clan_cli.vars.generator import dependencies_as_dir
|
|
||||||
|
|
||||||
decrypted_dependencies = {
|
decrypted_dependencies = {
|
||||||
"gen_1": {
|
"gen_1": {
|
||||||
"var_1a": b"var_1a",
|
"var_1a": b"var_1a",
|
||||||
@@ -506,7 +506,6 @@ def test_generate_secret_var_password_store(
|
|||||||
monkeypatch.setenv("PASSWORD_STORE_DIR", str(password_store_dir))
|
monkeypatch.setenv("PASSWORD_STORE_DIR", str(password_store_dir))
|
||||||
|
|
||||||
# Initialize password store as a git repository
|
# Initialize password store as a git repository
|
||||||
import subprocess
|
|
||||||
|
|
||||||
subprocess.run(["git", "init"], cwd=password_store_dir, check=True)
|
subprocess.run(["git", "init"], cwd=password_store_dir, check=True)
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
@@ -613,8 +612,6 @@ def test_generate_secret_for_multiple_machines(
|
|||||||
) -> None:
|
) -> None:
|
||||||
flake = flake_with_sops
|
flake = flake_with_sops
|
||||||
|
|
||||||
from clan_lib.nix import nix_config
|
|
||||||
|
|
||||||
local_system = nix_config()["system"]
|
local_system = nix_config()["system"]
|
||||||
|
|
||||||
machine1_config = flake.machines["machine1"]
|
machine1_config = flake.machines["machine1"]
|
||||||
@@ -1101,8 +1098,6 @@ def test_create_sops_age_secrets(
|
|||||||
# check private key exists
|
# check private key exists
|
||||||
assert (flake.temporary_home / ".config" / "sops" / "age" / "keys.txt").is_file()
|
assert (flake.temporary_home / ".config" / "sops" / "age" / "keys.txt").is_file()
|
||||||
# it should still work, even if the keys already exist
|
# it should still work, even if the keys already exist
|
||||||
import shutil
|
|
||||||
|
|
||||||
shutil.rmtree(flake.path / "sops" / "users" / "user")
|
shutil.rmtree(flake.path / "sops" / "users" / "user")
|
||||||
cli.run(["vars", "keygen", "--flake", str(flake.path), "--user", "user"])
|
cli.run(["vars", "keygen", "--flake", str(flake.path), "--user", "user"])
|
||||||
# check public key exists
|
# check public key exists
|
||||||
|
|||||||
@@ -142,8 +142,6 @@ class StoreBase(ABC):
|
|||||||
value: bytes,
|
value: bytes,
|
||||||
is_migration: bool = False,
|
is_migration: bool = False,
|
||||||
) -> list[Path]:
|
) -> list[Path]:
|
||||||
from clan_lib.machines.machines import Machine
|
|
||||||
|
|
||||||
changed_files: list[Path] = []
|
changed_files: list[Path] = []
|
||||||
|
|
||||||
# if generator was switched from shared to per-machine or vice versa,
|
# if generator was switched from shared to per-machine or vice versa,
|
||||||
@@ -169,6 +167,8 @@ class StoreBase(ABC):
|
|||||||
if generator.machine is None:
|
if generator.machine is None:
|
||||||
log_info = log.info
|
log_info = log.info
|
||||||
else:
|
else:
|
||||||
|
from clan_lib.machines.machines import Machine # noqa: PLC0415
|
||||||
|
|
||||||
machine = Machine(name=generator.machine, flake=self.flake)
|
machine = Machine(name=generator.machine, flake=self.flake)
|
||||||
log_info = machine.info
|
log_info = machine.info
|
||||||
if self.is_secret_store:
|
if self.is_secret_store:
|
||||||
|
|||||||
@@ -32,13 +32,14 @@ def vars_status(
|
|||||||
flake: Flake,
|
flake: Flake,
|
||||||
generator_name: None | str = None,
|
generator_name: None | str = None,
|
||||||
) -> VarStatus:
|
) -> VarStatus:
|
||||||
|
from clan_cli.vars.generator import Generator # noqa: PLC0415
|
||||||
|
|
||||||
machine = Machine(name=machine_name, flake=flake)
|
machine = Machine(name=machine_name, flake=flake)
|
||||||
missing_secret_vars = []
|
missing_secret_vars = []
|
||||||
missing_public_vars = []
|
missing_public_vars = []
|
||||||
# signals if a var needs to be updated (eg. needs re-encryption due to new users added)
|
# signals if a var needs to be updated (eg. needs re-encryption due to new users added)
|
||||||
unfixed_secret_vars = []
|
unfixed_secret_vars = []
|
||||||
invalid_generators = []
|
invalid_generators = []
|
||||||
from clan_cli.vars.generator import Generator
|
|
||||||
|
|
||||||
generators = Generator.get_machine_generators([machine.name], machine.flake)
|
generators = Generator.get_machine_generators([machine.name], machine.flake)
|
||||||
if generator_name:
|
if generator_name:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import argparse
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from clan_cli.completions import add_dynamic_completer, complete_machines
|
from clan_cli.completions import add_dynamic_completer, complete_machines
|
||||||
|
from clan_cli.vars.generator import Generator
|
||||||
from clan_lib.errors import ClanError
|
from clan_lib.errors import ClanError
|
||||||
from clan_lib.flake import require_flake
|
from clan_lib.flake import require_flake
|
||||||
from clan_lib.machines.machines import Machine
|
from clan_lib.machines.machines import Machine
|
||||||
@@ -10,8 +11,6 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def fix_vars(machine: Machine, generator_name: None | str = None) -> None:
|
def fix_vars(machine: Machine, generator_name: None | str = None) -> None:
|
||||||
from clan_cli.vars.generator import Generator
|
|
||||||
|
|
||||||
generators = Generator.get_machine_generators([machine.name], machine.flake)
|
generators = Generator.get_machine_generators([machine.name], machine.flake)
|
||||||
if generator_name:
|
if generator_name:
|
||||||
for generator in generators:
|
for generator in generators:
|
||||||
|
|||||||
@@ -1,23 +1,31 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from contextlib import ExitStack
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from clan_lib import bwrap
|
||||||
|
from clan_lib.cmd import RunOpts, run
|
||||||
from clan_lib.errors import ClanError
|
from clan_lib.errors import ClanError
|
||||||
from clan_lib.nix import nix_test_store
|
from clan_lib.git import commit_files
|
||||||
|
from clan_lib.nix import nix_config, nix_shell, nix_test_store
|
||||||
|
|
||||||
from .check import check_vars
|
from .check import check_vars
|
||||||
from .prompt import Prompt
|
from .prompt import Prompt, ask
|
||||||
from .var import Var
|
from .var import Var
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from clan_lib.flake import Flake
|
from clan_lib.flake import Flake
|
||||||
from clan_lib.machines.machines import Machine
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ._types import StoreBase
|
from ._types import StoreBase
|
||||||
|
|
||||||
|
from clan_lib.machines.machines import Machine
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -91,8 +99,6 @@ class Generator:
|
|||||||
list[Generator]: A list of (unsorted) generators for the machine.
|
list[Generator]: A list of (unsorted) generators for the machine.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from clan_lib.nix import nix_config
|
|
||||||
|
|
||||||
config = nix_config()
|
config = nix_config()
|
||||||
system = config["system"]
|
system = config["system"]
|
||||||
|
|
||||||
@@ -125,8 +131,6 @@ class Generator:
|
|||||||
files_selector,
|
files_selector,
|
||||||
)
|
)
|
||||||
|
|
||||||
from clan_lib.machines.machines import Machine
|
|
||||||
|
|
||||||
machine = Machine(name=machine_name, flake=flake)
|
machine = Machine(name=machine_name, flake=flake)
|
||||||
pub_store = machine.public_vars_store
|
pub_store = machine.public_vars_store
|
||||||
sec_store = machine.secret_vars_store
|
sec_store = machine.secret_vars_store
|
||||||
@@ -207,8 +211,6 @@ class Generator:
|
|||||||
if self._flake is None:
|
if self._flake is None:
|
||||||
msg = "Flake cannot be None"
|
msg = "Flake cannot be None"
|
||||||
raise ClanError(msg)
|
raise ClanError(msg)
|
||||||
from clan_lib.machines.machines import Machine
|
|
||||||
|
|
||||||
machine = Machine(name=self.machine, flake=self._flake)
|
machine = Machine(name=self.machine, flake=self._flake)
|
||||||
output = Path(
|
output = Path(
|
||||||
machine.select(
|
machine.select(
|
||||||
@@ -226,8 +228,6 @@ class Generator:
|
|||||||
if self._flake is None:
|
if self._flake is None:
|
||||||
msg = "Flake cannot be None"
|
msg = "Flake cannot be None"
|
||||||
raise ClanError(msg)
|
raise ClanError(msg)
|
||||||
from clan_lib.machines.machines import Machine
|
|
||||||
|
|
||||||
machine = Machine(name=self.machine, flake=self._flake)
|
machine = Machine(name=self.machine, flake=self._flake)
|
||||||
return machine.select(
|
return machine.select(
|
||||||
f'config.clan.core.vars.generators."{self.name}".validationHash',
|
f'config.clan.core.vars.generators."{self.name}".validationHash',
|
||||||
@@ -250,8 +250,6 @@ class Generator:
|
|||||||
Dictionary mapping generator names to their variable values
|
Dictionary mapping generator names to their variable values
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from clan_lib.errors import ClanError
|
|
||||||
|
|
||||||
generators = self.get_machine_generators([machine.name], machine.flake)
|
generators = self.get_machine_generators([machine.name], machine.flake)
|
||||||
result: dict[str, dict[str, bytes]] = {}
|
result: dict[str, dict[str, bytes]] = {}
|
||||||
|
|
||||||
@@ -297,8 +295,6 @@ class Generator:
|
|||||||
Dictionary mapping prompt names to their values
|
Dictionary mapping prompt names to their values
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from .prompt import ask
|
|
||||||
|
|
||||||
prompt_values: dict[str, str] = {}
|
prompt_values: dict[str, str] = {}
|
||||||
for prompt in self.prompts:
|
for prompt in self.prompts:
|
||||||
var_id = f"{self.name}/{prompt.name}"
|
var_id = f"{self.name}/{prompt.name}"
|
||||||
@@ -323,17 +319,6 @@ class Generator:
|
|||||||
no_sandbox: Whether to disable sandboxing when executing the generator
|
no_sandbox: Whether to disable sandboxing when executing the generator
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from contextlib import ExitStack
|
|
||||||
from pathlib import Path
|
|
||||||
from tempfile import TemporaryDirectory
|
|
||||||
|
|
||||||
from clan_lib import bwrap
|
|
||||||
from clan_lib.cmd import RunOpts, run
|
|
||||||
from clan_lib.errors import ClanError
|
|
||||||
from clan_lib.git import commit_files
|
|
||||||
|
|
||||||
if prompt_values is None:
|
if prompt_values is None:
|
||||||
prompt_values = self.ask_prompts()
|
prompt_values = self.ask_prompts()
|
||||||
|
|
||||||
@@ -353,10 +338,6 @@ class Generator:
|
|||||||
|
|
||||||
def bubblewrap_cmd(generator: str, tmpdir: Path) -> list[str]:
|
def bubblewrap_cmd(generator: str, tmpdir: Path) -> list[str]:
|
||||||
"""Helper function to create bubblewrap command."""
|
"""Helper function to create bubblewrap command."""
|
||||||
import shutil
|
|
||||||
|
|
||||||
from clan_lib.nix import nix_shell, nix_test_store
|
|
||||||
|
|
||||||
test_store = nix_test_store()
|
test_store = nix_test_store()
|
||||||
real_bash_path = Path("bash")
|
real_bash_path = Path("bash")
|
||||||
if os.environ.get("IN_NIX_SANDBOX"):
|
if os.environ.get("IN_NIX_SANDBOX"):
|
||||||
@@ -414,7 +395,7 @@ class Generator:
|
|||||||
if sys.platform == "linux" and bwrap.bubblewrap_works():
|
if sys.platform == "linux" and bwrap.bubblewrap_works():
|
||||||
cmd = bubblewrap_cmd(str(final_script), tmpdir)
|
cmd = bubblewrap_cmd(str(final_script), tmpdir)
|
||||||
elif sys.platform == "darwin":
|
elif sys.platform == "darwin":
|
||||||
from clan_lib.sandbox_exec import sandbox_exec_cmd
|
from clan_lib.sandbox_exec import sandbox_exec_cmd # noqa: PLC0415
|
||||||
|
|
||||||
cmd = stack.enter_context(sandbox_exec_cmd(str(final_script), tmpdir))
|
cmd = stack.enter_context(sandbox_exec_cmd(str(final_script), tmpdir))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from tempfile import TemporaryDirectory
|
|||||||
|
|
||||||
from clan_cli.vars._types import StoreBase
|
from clan_cli.vars._types import StoreBase
|
||||||
from clan_cli.vars.generator import Generator, Var
|
from clan_cli.vars.generator import Generator, Var
|
||||||
|
from clan_lib.cmd import Log, RunOpts
|
||||||
from clan_lib.flake import Flake
|
from clan_lib.flake import Flake
|
||||||
from clan_lib.ssh.host import Host
|
from clan_lib.ssh.host import Host
|
||||||
from clan_lib.ssh.upload import upload
|
from clan_lib.ssh.upload import upload
|
||||||
@@ -162,8 +163,6 @@ class SecretStore(StoreBase):
|
|||||||
if not git_hash:
|
if not git_hash:
|
||||||
return b""
|
return b""
|
||||||
|
|
||||||
from clan_cli.vars.generator import Generator
|
|
||||||
|
|
||||||
generators = Generator.get_machine_generators([machine], self.flake)
|
generators = Generator.get_machine_generators([machine], self.flake)
|
||||||
manifest = [
|
manifest = [
|
||||||
f"{generator.name}/{file.name}".encode()
|
f"{generator.name}/{file.name}".encode()
|
||||||
@@ -179,8 +178,6 @@ class SecretStore(StoreBase):
|
|||||||
if not local_hash:
|
if not local_hash:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
from clan_lib.cmd import Log, RunOpts
|
|
||||||
|
|
||||||
remote_hash = host.run(
|
remote_hash = host.run(
|
||||||
[
|
[
|
||||||
"cat",
|
"cat",
|
||||||
@@ -195,8 +192,6 @@ class SecretStore(StoreBase):
|
|||||||
return local_hash != remote_hash.encode()
|
return local_hash != remote_hash.encode()
|
||||||
|
|
||||||
def populate_dir(self, machine: str, output_dir: Path, phases: list[str]) -> None:
|
def populate_dir(self, machine: str, output_dir: Path, phases: list[str]) -> None:
|
||||||
from clan_cli.vars.generator import Generator
|
|
||||||
|
|
||||||
vars_generators = Generator.get_machine_generators([machine], self.flake)
|
vars_generators = Generator.get_machine_generators([machine], self.flake)
|
||||||
if "users" in phases:
|
if "users" in phases:
|
||||||
with tarfile.open(
|
with tarfile.open(
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class SecretStore(StoreBase):
|
|||||||
def ensure_machine_key(self, machine: str) -> None:
|
def ensure_machine_key(self, machine: str) -> None:
|
||||||
"""Ensure machine has sops keys initialized."""
|
"""Ensure machine has sops keys initialized."""
|
||||||
# no need to generate keys if we don't manage secrets
|
# no need to generate keys if we don't manage secrets
|
||||||
from clan_cli.vars.generator import Generator
|
from clan_cli.vars.generator import Generator # noqa: PLC0415
|
||||||
|
|
||||||
vars_generators = Generator.get_machine_generators([machine], self.flake)
|
vars_generators = Generator.get_machine_generators([machine], self.flake)
|
||||||
if not vars_generators:
|
if not vars_generators:
|
||||||
@@ -141,7 +141,7 @@ class SecretStore(StoreBase):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
if generators is None:
|
if generators is None:
|
||||||
from clan_cli.vars.generator import Generator
|
from clan_cli.vars.generator import Generator # noqa: PLC0415
|
||||||
|
|
||||||
generators = Generator.get_machine_generators([machine], self.flake)
|
generators = Generator.get_machine_generators([machine], self.flake)
|
||||||
file_found = False
|
file_found = False
|
||||||
@@ -219,7 +219,7 @@ class SecretStore(StoreBase):
|
|||||||
return [store_folder]
|
return [store_folder]
|
||||||
|
|
||||||
def populate_dir(self, machine: str, output_dir: Path, phases: list[str]) -> None:
|
def populate_dir(self, machine: str, output_dir: Path, phases: list[str]) -> None:
|
||||||
from clan_cli.vars.generator import Generator
|
from clan_cli.vars.generator import Generator # noqa: PLC0415
|
||||||
|
|
||||||
vars_generators = Generator.get_machine_generators([machine], self.flake)
|
vars_generators = Generator.get_machine_generators([machine], self.flake)
|
||||||
if "users" in phases or "services" in phases:
|
if "users" in phases or "services" in phases:
|
||||||
@@ -291,7 +291,7 @@ class SecretStore(StoreBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def collect_keys_for_secret(self, machine: str, path: Path) -> set[sops.SopsKey]:
|
def collect_keys_for_secret(self, machine: str, path: Path) -> set[sops.SopsKey]:
|
||||||
from clan_cli.secrets.secrets import (
|
from clan_cli.secrets.secrets import ( # noqa: PLC0415
|
||||||
collect_keys_for_path,
|
collect_keys_for_path,
|
||||||
collect_keys_for_type,
|
collect_keys_for_type,
|
||||||
)
|
)
|
||||||
@@ -352,10 +352,10 @@ class SecretStore(StoreBase):
|
|||||||
ClanError: If the specified file_name is not found
|
ClanError: If the specified file_name is not found
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from clan_cli.secrets.secrets import update_keys
|
from clan_cli.secrets.secrets import update_keys # noqa: PLC0415
|
||||||
|
|
||||||
if generators is None:
|
if generators is None:
|
||||||
from clan_cli.vars.generator import Generator
|
from clan_cli.vars.generator import Generator # noqa: PLC0415
|
||||||
|
|
||||||
generators = Generator.get_machine_generators([machine], self.flake)
|
generators = Generator.get_machine_generators([machine], self.flake)
|
||||||
file_found = False
|
file_found = False
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from clan_lib.async_run import get_current_thread_opkey
|
|||||||
from clan_lib.errors import ClanError
|
from clan_lib.errors import ClanError
|
||||||
|
|
||||||
from .serde import dataclass_to_dict, from_dict, sanitize_string
|
from .serde import dataclass_to_dict, from_dict, sanitize_string
|
||||||
|
from .type_to_jsonschema import JSchemaTypeError, type_to_dict
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -201,10 +202,6 @@ API.register(get_system_file)
|
|||||||
return fn
|
return fn
|
||||||
|
|
||||||
def to_json_schema(self) -> dict[str, Any]:
|
def to_json_schema(self) -> dict[str, Any]:
|
||||||
from typing import get_type_hints
|
|
||||||
|
|
||||||
from .type_to_jsonschema import JSchemaTypeError, type_to_dict
|
|
||||||
|
|
||||||
api_schema: dict[str, Any] = {
|
api_schema: dict[str, Any] = {
|
||||||
"$comment": "An object containing API methods. ",
|
"$comment": "An object containing API methods. ",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -268,8 +265,6 @@ API.register(get_system_file)
|
|||||||
return api_schema
|
return api_schema
|
||||||
|
|
||||||
def get_method_argtype(self, method_name: str, arg_name: str) -> Any:
|
def get_method_argtype(self, method_name: str, arg_name: str) -> Any:
|
||||||
from inspect import signature
|
|
||||||
|
|
||||||
func = self._registry.get(method_name, None)
|
func = self._registry.get(method_name, None)
|
||||||
if not func:
|
if not func:
|
||||||
msg = f"API Method {method_name} not found in registry. Available methods: {list(self._registry.keys())}"
|
msg = f"API Method {method_name} not found in registry. Available methods: {list(self._registry.keys())}"
|
||||||
@@ -313,9 +308,9 @@ def load_in_all_api_functions() -> None:
|
|||||||
We have to make sure python loads every wrapped function at least once.
|
We have to make sure python loads every wrapped function at least once.
|
||||||
This is done by importing all modules from the clan_lib and clan_cli packages.
|
This is done by importing all modules from the clan_lib and clan_cli packages.
|
||||||
"""
|
"""
|
||||||
import clan_cli
|
import clan_cli # noqa: PLC0415 # Avoid circular imports - many modules import from clan_lib.api
|
||||||
|
|
||||||
import clan_lib
|
import clan_lib # noqa: PLC0415 # Avoid circular imports - many modules import from clan_lib.api
|
||||||
|
|
||||||
import_all_modules_from_package(clan_lib)
|
import_all_modules_from_package(clan_lib)
|
||||||
import_all_modules_from_package(clan_cli)
|
import_all_modules_from_package(clan_cli)
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ from pathlib import Path
|
|||||||
from typing import Any, Literal
|
from typing import Any, Literal
|
||||||
|
|
||||||
from clan_lib.cmd import RunOpts, run
|
from clan_lib.cmd import RunOpts, run
|
||||||
from clan_lib.flake import Flake
|
from clan_lib.flake.flake import Flake
|
||||||
from clan_lib.nix import nix_shell
|
|
||||||
|
|
||||||
from . import API
|
from . import API
|
||||||
|
|
||||||
@@ -89,6 +88,8 @@ def list_system_storage_devices() -> Blockdevices:
|
|||||||
A list of detected block devices with metadata like size, path, type, etc.
|
A list of detected block devices with metadata like size, path, type, etc.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from clan_lib.nix import nix_shell # noqa: PLC0415
|
||||||
|
|
||||||
cmd = nix_shell(
|
cmd = nix_shell(
|
||||||
["util-linux"],
|
["util-linux"],
|
||||||
[
|
[
|
||||||
@@ -123,7 +124,7 @@ def get_clan_directory_relative(flake: Flake) -> str:
|
|||||||
ClanError: If the flake evaluation fails or directories cannot be found
|
ClanError: If the flake evaluation fails or directories cannot be found
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from clan_lib.dirs import get_clan_directories
|
from clan_lib.dirs import get_clan_directories # noqa: PLC0415
|
||||||
|
|
||||||
_, relative_dir = get_clan_directories(flake)
|
_, relative_dir = get_clan_directories(flake)
|
||||||
return relative_dir
|
return relative_dir
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Literal, TypedDict
|
from typing import Any, Literal, TypedDict
|
||||||
|
|
||||||
@@ -334,8 +335,6 @@ def test_literal_field() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_enum_roundtrip() -> None:
|
def test_enum_roundtrip() -> None:
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
class MyEnum(Enum):
|
class MyEnum(Enum):
|
||||||
FOO = "abc"
|
FOO = "abc"
|
||||||
BAR = 2
|
BAR = 2
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
# Functions to test
|
# Functions to test
|
||||||
from clan_lib.api import (
|
from clan_lib.api import (
|
||||||
@@ -124,8 +125,6 @@ def test_filters_null_fields() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_custom_enum() -> None:
|
def test_custom_enum() -> None:
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
class CustomEnum(Enum):
|
class CustomEnum(Enum):
|
||||||
FOO = "foo"
|
FOO = "foo"
|
||||||
BAR = "bar"
|
BAR = "bar"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Any, NotRequired, Required
|
from enum import Enum
|
||||||
|
from typing import Any, Generic, NotRequired, Required, TypedDict, TypeVar
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@@ -27,8 +28,6 @@ def test_simple_primitives() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_enum_type() -> None:
|
def test_enum_type() -> None:
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
class Color(Enum):
|
class Color(Enum):
|
||||||
RED = "red"
|
RED = "red"
|
||||||
GREEN = "green"
|
GREEN = "green"
|
||||||
@@ -224,8 +223,6 @@ def test_nested_open_dicts() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_type_variables() -> None:
|
def test_type_variables() -> None:
|
||||||
from typing import Generic, TypeVar
|
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -254,8 +251,6 @@ def test_type_variables() -> None:
|
|||||||
def test_type_variable_nested_scopes() -> None:
|
def test_type_variable_nested_scopes() -> None:
|
||||||
# Define two type variables with the same name "T" but in different scopes
|
# Define two type variables with the same name "T" but in different scopes
|
||||||
|
|
||||||
from typing import Generic, TypeVar
|
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -284,8 +279,6 @@ def test_type_variable_nested_scopes() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_total_typed_dict() -> None:
|
def test_total_typed_dict() -> None:
|
||||||
from typing import TypedDict
|
|
||||||
|
|
||||||
class ExampleTypedDict(TypedDict):
|
class ExampleTypedDict(TypedDict):
|
||||||
name: str
|
name: str
|
||||||
value: NotRequired[int]
|
value: NotRequired[int]
|
||||||
@@ -314,8 +307,6 @@ def test_total_typed_dict() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_open_typed_dict() -> None:
|
def test_open_typed_dict() -> None:
|
||||||
from typing import TypedDict
|
|
||||||
|
|
||||||
class ExampleTypedDict(TypedDict, total=False):
|
class ExampleTypedDict(TypedDict, total=False):
|
||||||
name: Required[str]
|
name: Required[str]
|
||||||
value: int
|
value: int
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
@@ -6,7 +7,9 @@ from enum import Enum
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Protocol
|
from typing import TYPE_CHECKING, Protocol
|
||||||
|
|
||||||
|
from clan_lib.cmd import run
|
||||||
from clan_lib.errors import ClanError
|
from clan_lib.errors import ClanError
|
||||||
|
from clan_lib.nix import nix_eval
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from clan_lib.flake import Flake
|
from clan_lib.flake import Flake
|
||||||
@@ -198,12 +201,6 @@ def get_clan_directories(flake: "Flake") -> tuple[str, str]:
|
|||||||
ClanError: If the flake evaluation fails or directories cannot be found
|
ClanError: If the flake evaluation fails or directories cannot be found
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import json
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from clan_lib.cmd import run
|
|
||||||
from clan_lib.nix import nix_eval
|
|
||||||
|
|
||||||
# Get the source directory from nix store
|
# Get the source directory from nix store
|
||||||
root_directory = flake.select("sourceInfo")
|
root_directory = flake.select("sourceInfo")
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,16 @@ from pathlib import Path
|
|||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from clan_lib.cmd import Log, RunOpts, run
|
||||||
|
from clan_lib.dirs import select_source, user_cache_dir
|
||||||
from clan_lib.errors import ClanCmdError, ClanError
|
from clan_lib.errors import ClanCmdError, ClanError
|
||||||
|
from clan_lib.nix import (
|
||||||
|
nix_build,
|
||||||
|
nix_command,
|
||||||
|
nix_config,
|
||||||
|
nix_metadata,
|
||||||
|
nix_test_store,
|
||||||
|
)
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -851,11 +860,6 @@ class Flake:
|
|||||||
|
|
||||||
def prefetch(self) -> None:
|
def prefetch(self) -> None:
|
||||||
"""Loads the flake into the store and populates self.store_path and self.hash such that the flake can evaluate locally and offline"""
|
"""Loads the flake into the store and populates self.store_path and self.hash such that the flake can evaluate locally and offline"""
|
||||||
from clan_lib.cmd import RunOpts, run
|
|
||||||
from clan_lib.nix import (
|
|
||||||
nix_command,
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.nix_options is None:
|
if self.nix_options is None:
|
||||||
self.nix_options = []
|
self.nix_options = []
|
||||||
|
|
||||||
@@ -895,11 +899,6 @@ class Flake:
|
|||||||
|
|
||||||
This method is used to refresh the cache by reloading it from the flake.
|
This method is used to refresh the cache by reloading it from the flake.
|
||||||
"""
|
"""
|
||||||
from clan_lib.dirs import user_cache_dir
|
|
||||||
from clan_lib.nix import (
|
|
||||||
nix_metadata,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.prefetch()
|
self.prefetch()
|
||||||
|
|
||||||
self._cache = FlakeCache()
|
self._cache = FlakeCache()
|
||||||
@@ -951,14 +950,6 @@ class Flake:
|
|||||||
AssertionError: If the cache or flake cache path is not properly initialized.
|
AssertionError: If the cache or flake cache path is not properly initialized.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from clan_lib.cmd import Log, RunOpts, run
|
|
||||||
from clan_lib.dirs import select_source
|
|
||||||
from clan_lib.nix import (
|
|
||||||
nix_build,
|
|
||||||
nix_config,
|
|
||||||
nix_test_store,
|
|
||||||
)
|
|
||||||
|
|
||||||
if self._cache is None:
|
if self._cache is None:
|
||||||
self.invalidate_cache()
|
self.invalidate_cache()
|
||||||
if self._cache is None:
|
if self._cache is None:
|
||||||
@@ -1125,8 +1116,6 @@ class Flake:
|
|||||||
apply: Optional function to apply to the result
|
apply: Optional function to apply to the result
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from clan_lib.nix import nix_config
|
|
||||||
|
|
||||||
config = nix_config()
|
config = nix_config()
|
||||||
system = config["system"]
|
system = config["system"]
|
||||||
|
|
||||||
|
|||||||
@@ -360,11 +360,6 @@ def test_store_path_with_line_numbers_not_wrapped() -> None:
|
|||||||
|
|
||||||
def test_store_reference_helpers() -> None:
|
def test_store_reference_helpers() -> None:
|
||||||
"""Test the store reference helper functions."""
|
"""Test the store reference helper functions."""
|
||||||
from clan_lib.flake.flake import (
|
|
||||||
find_store_references,
|
|
||||||
is_pure_store_path,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Test find_store_references
|
# Test find_store_references
|
||||||
assert find_store_references("/nix/store/abc123-pkg") == ["/nix/store/abc123-pkg"]
|
assert find_store_references("/nix/store/abc123-pkg") == ["/nix/store/abc123-pkg"]
|
||||||
assert find_store_references("/nix/store/abc123-file.nix:42") == [
|
assert find_store_references("/nix/store/abc123-file.nix:42") == [
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from tempfile import TemporaryDirectory
|
|||||||
from typing import Any, Literal
|
from typing import Any, Literal
|
||||||
|
|
||||||
from clan_cli.facts.generate import generate_facts
|
from clan_cli.facts.generate import generate_facts
|
||||||
|
from clan_cli.vars.generator import Generator
|
||||||
from clan_cli.vars.upload import populate_secret_vars
|
from clan_cli.vars.upload import populate_secret_vars
|
||||||
|
|
||||||
from clan_lib.api import API
|
from clan_lib.api import API
|
||||||
@@ -116,8 +117,6 @@ def run_machine_flash(
|
|||||||
"users": {"root": {"openssh": {"authorizedKeys": {"keys": root_keys}}}},
|
"users": {"root": {"openssh": {"authorizedKeys": {"keys": root_keys}}}},
|
||||||
}
|
}
|
||||||
|
|
||||||
from clan_cli.vars.generator import Generator
|
|
||||||
|
|
||||||
for generator in Generator.get_machine_generators(
|
for generator in Generator.get_machine_generators(
|
||||||
[machine.name], machine.flake
|
[machine.name], machine.flake
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import importlib.util
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
@@ -52,8 +54,6 @@ def test_import_with_source(tmp_path: Path) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Add the temp directory to sys.path
|
# Add the temp directory to sys.path
|
||||||
import sys
|
|
||||||
|
|
||||||
sys.path.insert(0, str(tmp_path))
|
sys.path.insert(0, str(tmp_path))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -130,9 +130,6 @@ def test_import_with_source_with_args() -> None:
|
|||||||
temp_file = Path(f.name)
|
temp_file = Path(f.name)
|
||||||
|
|
||||||
# Import module dynamically
|
# Import module dynamically
|
||||||
import importlib.util
|
|
||||||
import sys
|
|
||||||
|
|
||||||
spec = importlib.util.spec_from_file_location("temp_module", temp_file)
|
spec = importlib.util.spec_from_file_location("temp_module", temp_file)
|
||||||
assert spec is not None
|
assert spec is not None
|
||||||
assert spec.loader is not None
|
assert spec.loader is not None
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from contextlib import contextmanager
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from clan_lib.dirs import user_history_file
|
|
||||||
from clan_lib.jsonrpc import ClanJSONEncoder
|
from clan_lib.jsonrpc import ClanJSONEncoder
|
||||||
|
|
||||||
|
|
||||||
@@ -19,11 +18,15 @@ def locked_open(filename: Path, mode: str = "r") -> Generator:
|
|||||||
|
|
||||||
|
|
||||||
def write_history_file(data: Any) -> None:
|
def write_history_file(data: Any) -> None:
|
||||||
|
from clan_lib.dirs import user_history_file # noqa: PLC0415
|
||||||
|
|
||||||
with locked_open(user_history_file(), "w+") as f:
|
with locked_open(user_history_file(), "w+") as f:
|
||||||
f.write(json.dumps(data, cls=ClanJSONEncoder, indent=4))
|
f.write(json.dumps(data, cls=ClanJSONEncoder, indent=4))
|
||||||
|
|
||||||
|
|
||||||
def read_history_file() -> list[dict]:
|
def read_history_file() -> list[dict]:
|
||||||
|
from clan_lib.dirs import user_history_file # noqa: PLC0415
|
||||||
|
|
||||||
with locked_open(user_history_file(), "r") as f:
|
with locked_open(user_history_file(), "r") as f:
|
||||||
content: str = f.read()
|
content: str = f.read()
|
||||||
parsed: list[dict] = json.loads(content)
|
parsed: list[dict] = json.loads(content)
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ Tests are based on actual usage patterns from example_usage.py and api.py.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from clan_lib.log_manager import (
|
from clan_lib.log_manager import (
|
||||||
|
LogFile,
|
||||||
LogGroupConfig,
|
LogGroupConfig,
|
||||||
LogManager,
|
LogManager,
|
||||||
is_correct_day_format,
|
is_correct_day_format,
|
||||||
@@ -472,8 +474,6 @@ class TestLogFileSorting:
|
|||||||
configured_log_manager: LogManager,
|
configured_log_manager: LogManager,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that LogFiles are sorted by datetime (newest first)."""
|
"""Test that LogFiles are sorted by datetime (newest first)."""
|
||||||
from clan_lib.log_manager import LogFile
|
|
||||||
|
|
||||||
# Create LogFiles with different times (same date)
|
# Create LogFiles with different times (same date)
|
||||||
newer_file = LogFile(
|
newer_file = LogFile(
|
||||||
op_key="test_op",
|
op_key="test_op",
|
||||||
@@ -508,8 +508,6 @@ class TestLogFileSorting:
|
|||||||
configured_log_manager: LogManager,
|
configured_log_manager: LogManager,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that LogFiles are sorted by date (newer dates first)."""
|
"""Test that LogFiles are sorted by date (newer dates first)."""
|
||||||
from clan_lib.log_manager import LogFile
|
|
||||||
|
|
||||||
# Create LogFiles with different dates
|
# Create LogFiles with different dates
|
||||||
newer_date_file = LogFile(
|
newer_date_file = LogFile(
|
||||||
op_key="test_op",
|
op_key="test_op",
|
||||||
@@ -543,8 +541,6 @@ class TestLogFileSorting:
|
|||||||
configured_log_manager: LogManager,
|
configured_log_manager: LogManager,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that LogFiles with same datetime are sorted by group name (alphabetical)."""
|
"""Test that LogFiles with same datetime are sorted by group name (alphabetical)."""
|
||||||
from clan_lib.log_manager import LogFile
|
|
||||||
|
|
||||||
# Create LogFiles with same datetime but different groups
|
# Create LogFiles with same datetime but different groups
|
||||||
group_a_file = LogFile(
|
group_a_file = LogFile(
|
||||||
op_key="test_op",
|
op_key="test_op",
|
||||||
@@ -579,8 +575,6 @@ class TestLogFileSorting:
|
|||||||
configured_log_manager: LogManager,
|
configured_log_manager: LogManager,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that LogFiles with same datetime and group are sorted by func_name (alphabetical)."""
|
"""Test that LogFiles with same datetime and group are sorted by func_name (alphabetical)."""
|
||||||
from clan_lib.log_manager import LogFile
|
|
||||||
|
|
||||||
# Create LogFiles with same datetime and group but different func_names
|
# Create LogFiles with same datetime and group but different func_names
|
||||||
func_a_file = LogFile(
|
func_a_file = LogFile(
|
||||||
op_key="test_op",
|
op_key="test_op",
|
||||||
@@ -614,8 +608,6 @@ class TestLogFileSorting:
|
|||||||
configured_log_manager: LogManager,
|
configured_log_manager: LogManager,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that LogFiles with same datetime, group, and func_name are sorted by op_key (alphabetical)."""
|
"""Test that LogFiles with same datetime, group, and func_name are sorted by op_key (alphabetical)."""
|
||||||
from clan_lib.log_manager import LogFile
|
|
||||||
|
|
||||||
# Create LogFiles identical except for op_key
|
# Create LogFiles identical except for op_key
|
||||||
op_a_file = LogFile(
|
op_a_file = LogFile(
|
||||||
op_key="op_a", # Should sort first alphabetically
|
op_key="op_a", # Should sort first alphabetically
|
||||||
@@ -649,8 +641,6 @@ class TestLogFileSorting:
|
|||||||
configured_log_manager: LogManager,
|
configured_log_manager: LogManager,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test complex sorting with multiple LogFiles demonstrating full sort order."""
|
"""Test complex sorting with multiple LogFiles demonstrating full sort order."""
|
||||||
from clan_lib.log_manager import LogFile
|
|
||||||
|
|
||||||
# Create multiple files with different characteristics
|
# Create multiple files with different characteristics
|
||||||
files = [
|
files = [
|
||||||
# Oldest datetime, should be last
|
# Oldest datetime, should be last
|
||||||
@@ -813,8 +803,6 @@ class TestLogFileSorting:
|
|||||||
"""Test that list_log_days returns days sorted newest first."""
|
"""Test that list_log_days returns days sorted newest first."""
|
||||||
del configured_log_manager # Unused but kept for API compatibility
|
del configured_log_manager # Unused but kept for API compatibility
|
||||||
# Create log files on different days by manipulating the date
|
# Create log files on different days by manipulating the date
|
||||||
import tempfile
|
|
||||||
|
|
||||||
# Create files with different dates manually to test sorting
|
# Create files with different dates manually to test sorting
|
||||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
base_dir = Path(tmp_dir)
|
base_dir = Path(tmp_dir)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class Machine:
|
|||||||
|
|
||||||
def get_inv_machine(self) -> "InventoryMachine":
|
def get_inv_machine(self) -> "InventoryMachine":
|
||||||
# Import on demand to avoid circular imports
|
# Import on demand to avoid circular imports
|
||||||
from clan_lib.machines.actions import get_machine
|
from clan_lib.machines.actions import get_machine # noqa: PLC0415
|
||||||
|
|
||||||
return get_machine(self.flake, self.name)
|
return get_machine(self.flake, self.name)
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ class Machine:
|
|||||||
return self.flake.path
|
return self.flake.path
|
||||||
|
|
||||||
def target_host(self) -> Remote:
|
def target_host(self) -> Remote:
|
||||||
from clan_lib.network.network import get_best_remote
|
from clan_lib.network.network import get_best_remote # noqa: PLC0415
|
||||||
|
|
||||||
with get_best_remote(self) as remote:
|
with get_best_remote(self) as remote:
|
||||||
return remote
|
return remote
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def _suggest_similar_names(
|
|||||||
|
|
||||||
|
|
||||||
def get_available_machines(flake: Flake) -> list[str]:
|
def get_available_machines(flake: Flake) -> list[str]:
|
||||||
from clan_lib.machines.list import list_machines
|
from clan_lib.machines.list import list_machines # noqa: PLC0415
|
||||||
|
|
||||||
machines = list_machines(flake)
|
machines = list_machines(flake)
|
||||||
return list(machines.keys())
|
return list(machines.keys())
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class Peer:
|
|||||||
_var: dict[str, str] = self._host["var"]
|
_var: dict[str, str] = self._host["var"]
|
||||||
machine_name = _var["machine"]
|
machine_name = _var["machine"]
|
||||||
generator = _var["generator"]
|
generator = _var["generator"]
|
||||||
from clan_lib.machines.machines import Machine
|
from clan_lib.machines.machines import Machine # noqa: PLC0415
|
||||||
|
|
||||||
machine = Machine(name=machine_name, flake=self.flake)
|
machine = Machine(name=machine_name, flake=self.flake)
|
||||||
var = get_machine_var(
|
var = get_machine_var(
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ class NetworkTechnology(NetworkTechnologyBase):
|
|||||||
yield network
|
yield network
|
||||||
|
|
||||||
def remote(self, peer: Peer) -> "Remote":
|
def remote(self, peer: Peer) -> "Remote":
|
||||||
from clan_lib.ssh.remote import Remote
|
|
||||||
|
|
||||||
return Remote(
|
return Remote(
|
||||||
address=peer.host,
|
address=peer.host,
|
||||||
command_prefix=peer.name,
|
command_prefix=peer.name,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import socket
|
||||||
import time
|
import time
|
||||||
from collections.abc import Iterator
|
from collections.abc import Iterator
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
@@ -70,8 +71,6 @@ class TorCheck:
|
|||||||
|
|
||||||
def tor_online_test(proxy_port: int) -> None:
|
def tor_online_test(proxy_port: int) -> None:
|
||||||
"""Tests if Tor is online by checking if we can establish a SOCKS5 connection."""
|
"""Tests if Tor is online by checking if we can establish a SOCKS5 connection."""
|
||||||
import socket
|
|
||||||
|
|
||||||
# Try to establish a SOCKS5 handshake with the Tor proxy
|
# Try to establish a SOCKS5 handshake with the Tor proxy
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.settimeout(2.0) # Short timeout for local connection
|
sock.settimeout(2.0) # Short timeout for local connection
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from pathlib import Path
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from clan_lib.cmd import run
|
from clan_lib.cmd import run
|
||||||
from clan_lib.dirs import nixpkgs_flake, nixpkgs_source
|
|
||||||
from clan_lib.errors import ClanError
|
from clan_lib.errors import ClanError
|
||||||
from clan_lib.locked_open import locked_open
|
from clan_lib.locked_open import locked_open
|
||||||
|
|
||||||
@@ -89,6 +88,8 @@ def nix_eval(flags: list[str]) -> list[str]:
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
if os.environ.get("IN_NIX_SANDBOX"):
|
if os.environ.get("IN_NIX_SANDBOX"):
|
||||||
|
from clan_lib.dirs import nixpkgs_source # noqa: PLC0415
|
||||||
|
|
||||||
return [
|
return [
|
||||||
*default_flags,
|
*default_flags,
|
||||||
"--override-input",
|
"--override-input",
|
||||||
@@ -168,6 +169,9 @@ def nix_shell(packages: list[str], cmd: list[str]) -> list[str]:
|
|||||||
] + [package for package in packages if "#" in package]
|
] + [package for package in packages if "#" in package]
|
||||||
if not missing_packages:
|
if not missing_packages:
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
|
from clan_lib.dirs import nixpkgs_flake # noqa: PLC0415
|
||||||
|
|
||||||
return [
|
return [
|
||||||
*nix_command(["shell", "--inputs-from", f"{nixpkgs_flake()!s}"]),
|
*nix_command(["shell", "--inputs-from", f"{nixpkgs_flake()!s}"]),
|
||||||
*missing_packages,
|
*missing_packages,
|
||||||
|
|||||||
@@ -459,12 +459,12 @@ class Remote:
|
|||||||
self,
|
self,
|
||||||
opts: "ConnectionOptions | None" = None,
|
opts: "ConnectionOptions | None" = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
from clan_lib.network.check import check_machine_ssh_reachable
|
from clan_lib.network.check import check_machine_ssh_reachable # noqa: PLC0415
|
||||||
|
|
||||||
return check_machine_ssh_reachable(self, opts)
|
return check_machine_ssh_reachable(self, opts)
|
||||||
|
|
||||||
def check_machine_ssh_login(self) -> None:
|
def check_machine_ssh_login(self) -> None:
|
||||||
from clan_lib.network.check import check_machine_ssh_login
|
from clan_lib.network.check import check_machine_ssh_login # noqa: PLC0415
|
||||||
|
|
||||||
return check_machine_ssh_login(self)
|
return check_machine_ssh_login(self)
|
||||||
|
|
||||||
@@ -521,7 +521,6 @@ def _parse_ssh_uri(
|
|||||||
raise ClanError(msg)
|
raise ClanError(msg)
|
||||||
hostname = result.hostname
|
hostname = result.hostname
|
||||||
port = result.port
|
port = result.port
|
||||||
from clan_lib.ssh.remote import Remote
|
|
||||||
|
|
||||||
return Remote(
|
return Remote(
|
||||||
address=hostname,
|
address=hostname,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
|
||||||
|
from clan_cli.vars import graph
|
||||||
from clan_cli.vars.generator import Generator, GeneratorKey
|
from clan_cli.vars.generator import Generator, GeneratorKey
|
||||||
from clan_cli.vars.graph import minimal_closure, requested_closure
|
from clan_cli.vars.graph import minimal_closure, requested_closure
|
||||||
from clan_cli.vars.migration import check_can_migrate, migrate_files
|
from clan_cli.vars.migration import check_can_migrate, migrate_files
|
||||||
@@ -31,8 +32,6 @@ def get_generators(
|
|||||||
List of generators based on the specified selection and closure mode.
|
List of generators based on the specified selection and closure mode.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from clan_cli.vars import graph
|
|
||||||
|
|
||||||
machine_names = [machine.name for machine in machines]
|
machine_names = [machine.name for machine in machines]
|
||||||
vars_generators = Generator.get_machine_generators(
|
vars_generators = Generator.get_machine_generators(
|
||||||
machine_names,
|
machine_names,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class MPProcess:
|
|||||||
def _set_proc_name(name: str) -> None:
|
def _set_proc_name(name: str) -> None:
|
||||||
if sys.platform != "linux":
|
if sys.platform != "linux":
|
||||||
return
|
return
|
||||||
import ctypes
|
import ctypes # noqa: PLC0415
|
||||||
|
|
||||||
# Define the prctl function with the appropriate arguments and return type
|
# Define the prctl function with the appropriate arguments and return type
|
||||||
libc = ctypes.CDLL("libc.so.6")
|
libc = ctypes.CDLL("libc.so.6")
|
||||||
|
|||||||
@@ -33,6 +33,15 @@ from gi.repository import GdkPixbuf, Gio, GLib, Gtk
|
|||||||
|
|
||||||
from clan_vm_manager.assets import loc
|
from clan_vm_manager.assets import loc
|
||||||
|
|
||||||
|
# Windows-specific imports
|
||||||
|
if sys.platform == "win32":
|
||||||
|
from ctypes import byref, sizeof, windll # type: ignore[attr-defined]
|
||||||
|
else:
|
||||||
|
# Create dummy objects for type checking on non-Windows systems
|
||||||
|
windll = None
|
||||||
|
byref = None # type: ignore[assignment]
|
||||||
|
sizeof = None # type: ignore[assignment]
|
||||||
|
|
||||||
|
|
||||||
# DUMMY IMPLEMENTATION
|
# DUMMY IMPLEMENTATION
|
||||||
################################################
|
################################################
|
||||||
@@ -747,12 +756,12 @@ class Win32Implementation(BaseImplementation):
|
|||||||
SM_CXSMICON = 49
|
SM_CXSMICON = 49
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
from ctypes import Structure
|
from ctypes import Structure # noqa: PLC0415
|
||||||
|
|
||||||
class WNDCLASSW(Structure):
|
class WNDCLASSW(Structure):
|
||||||
"""Windows class structure for window registration."""
|
"""Windows class structure for window registration."""
|
||||||
|
|
||||||
from ctypes import CFUNCTYPE, wintypes
|
from ctypes import CFUNCTYPE, wintypes # noqa: PLC0415
|
||||||
|
|
||||||
LPFN_WND_PROC = CFUNCTYPE(
|
LPFN_WND_PROC = CFUNCTYPE(
|
||||||
wintypes.INT,
|
wintypes.INT,
|
||||||
@@ -777,7 +786,7 @@ class Win32Implementation(BaseImplementation):
|
|||||||
class MENUITEMINFOW(Structure):
|
class MENUITEMINFOW(Structure):
|
||||||
"""Windows menu item information structure."""
|
"""Windows menu item information structure."""
|
||||||
|
|
||||||
from ctypes import wintypes
|
from ctypes import wintypes # noqa: PLC0415
|
||||||
|
|
||||||
_fields_: ClassVar = [
|
_fields_: ClassVar = [
|
||||||
("cb_size", wintypes.UINT),
|
("cb_size", wintypes.UINT),
|
||||||
@@ -797,7 +806,7 @@ class Win32Implementation(BaseImplementation):
|
|||||||
class NOTIFYICONDATAW(Structure):
|
class NOTIFYICONDATAW(Structure):
|
||||||
"""Windows notification icon data structure."""
|
"""Windows notification icon data structure."""
|
||||||
|
|
||||||
from ctypes import wintypes
|
from ctypes import wintypes # noqa: PLC0415
|
||||||
|
|
||||||
_fields_: ClassVar = [
|
_fields_: ClassVar = [
|
||||||
("cb_size", wintypes.DWORD),
|
("cb_size", wintypes.DWORD),
|
||||||
@@ -818,8 +827,6 @@ class Win32Implementation(BaseImplementation):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, application: Gtk.Application) -> None:
|
def __init__(self, application: Gtk.Application) -> None:
|
||||||
from ctypes import windll # type: ignore[attr-defined]
|
|
||||||
|
|
||||||
super().__init__(application)
|
super().__init__(application)
|
||||||
|
|
||||||
self._window_class: Any = None
|
self._window_class: Any = None
|
||||||
@@ -827,42 +834,45 @@ class Win32Implementation(BaseImplementation):
|
|||||||
self._notify_id = None
|
self._notify_id = None
|
||||||
self._h_icon = None
|
self._h_icon = None
|
||||||
self._menu = None
|
self._menu = None
|
||||||
self._wm_taskbarcreated = windll.user32.RegisterWindowMessageW("TaskbarCreated")
|
if sys.platform == "win32":
|
||||||
|
self._wm_taskbarcreated = windll.user32.RegisterWindowMessageW(
|
||||||
|
"TaskbarCreated"
|
||||||
|
) # type: ignore[attr-defined]
|
||||||
|
|
||||||
self._register_class()
|
self._register_class()
|
||||||
self._create_window()
|
self._create_window()
|
||||||
self.update_icon()
|
self.update_icon()
|
||||||
|
|
||||||
def _register_class(self) -> None:
|
def _register_class(self) -> None:
|
||||||
from ctypes import byref, windll # type: ignore[attr-defined]
|
if sys.platform != "win32":
|
||||||
|
return
|
||||||
|
|
||||||
self._window_class = self.WNDCLASSW( # type: ignore[attr-defined]
|
self._window_class = self.WNDCLASSW( # type: ignore[attr-defined]
|
||||||
style=(self.CS_VREDRAW | self.CS_HREDRAW),
|
style=(self.CS_VREDRAW | self.CS_HREDRAW),
|
||||||
lpfn_wnd_proc=self.WNDCLASSW.LPFN_WND_PROC(self.on_process_window_message), # type: ignore[attr-defined]
|
lpfn_wnd_proc=self.WNDCLASSW.LPFN_WND_PROC(self.on_process_window_message), # type: ignore[attr-defined]
|
||||||
h_cursor=windll.user32.LoadCursorW(0, self.IDC_ARROW),
|
h_cursor=windll.user32.LoadCursorW(0, self.IDC_ARROW), # type: ignore[attr-defined]
|
||||||
hbr_background=self.COLOR_WINDOW,
|
hbr_background=self.COLOR_WINDOW,
|
||||||
lpsz_class_name=self.WINDOW_CLASS_NAME,
|
lpsz_class_name=self.WINDOW_CLASS_NAME,
|
||||||
)
|
)
|
||||||
|
|
||||||
windll.user32.RegisterClassW(byref(self._window_class))
|
windll.user32.RegisterClassW(byref(self._window_class)) # type: ignore[attr-defined]
|
||||||
|
|
||||||
def _unregister_class(self):
|
def _unregister_class(self):
|
||||||
if self._window_class is None:
|
if self._window_class is None or sys.platform != "win32":
|
||||||
return
|
return
|
||||||
|
|
||||||
from ctypes import windll
|
windll.user32.UnregisterClassW( # type: ignore[attr-defined]
|
||||||
|
|
||||||
windll.user32.UnregisterClassW(
|
|
||||||
self.WINDOW_CLASS_NAME,
|
self.WINDOW_CLASS_NAME,
|
||||||
self._window_class.h_instance,
|
self._window_class.h_instance,
|
||||||
)
|
)
|
||||||
self._window_class = None
|
self._window_class = None
|
||||||
|
|
||||||
def _create_window(self) -> None:
|
def _create_window(self) -> None:
|
||||||
from ctypes import windll # type: ignore[attr-defined]
|
if sys.platform != "win32":
|
||||||
|
return
|
||||||
|
|
||||||
style = self.WS_OVERLAPPED | self.WS_SYSMENU
|
style = self.WS_OVERLAPPED | self.WS_SYSMENU
|
||||||
self._h_wnd = windll.user32.CreateWindowExW(
|
self._h_wnd = windll.user32.CreateWindowExW( # type: ignore[attr-defined]
|
||||||
0,
|
0,
|
||||||
self.WINDOW_CLASS_NAME,
|
self.WINDOW_CLASS_NAME,
|
||||||
self.WINDOW_CLASS_NAME,
|
self.WINDOW_CLASS_NAME,
|
||||||
@@ -877,15 +887,13 @@ class Win32Implementation(BaseImplementation):
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
||||||
windll.user32.UpdateWindow(self._h_wnd)
|
windll.user32.UpdateWindow(self._h_wnd) # type: ignore[attr-defined]
|
||||||
|
|
||||||
def _destroy_window(self):
|
def _destroy_window(self):
|
||||||
if self._h_wnd is None:
|
if self._h_wnd is None or sys.platform != "win32":
|
||||||
return
|
return
|
||||||
|
|
||||||
from ctypes import windll
|
windll.user32.DestroyWindow(self._h_wnd) # type: ignore[attr-defined]
|
||||||
|
|
||||||
windll.user32.DestroyWindow(self._h_wnd)
|
|
||||||
self._h_wnd = None
|
self._h_wnd = None
|
||||||
|
|
||||||
def _load_ico_buffer(self, icon_name, icon_size):
|
def _load_ico_buffer(self, icon_name, icon_size):
|
||||||
@@ -922,10 +930,11 @@ class Win32Implementation(BaseImplementation):
|
|||||||
return ico_buffer
|
return ico_buffer
|
||||||
|
|
||||||
def _load_h_icon(self, icon_name):
|
def _load_h_icon(self, icon_name):
|
||||||
from ctypes import windll
|
if sys.platform != "win32":
|
||||||
|
return None
|
||||||
|
|
||||||
# Attempt to load custom icons first
|
# Attempt to load custom icons first
|
||||||
icon_size = windll.user32.GetSystemMetrics(self.SM_CXSMICON)
|
icon_size = windll.user32.GetSystemMetrics(self.SM_CXSMICON) # type: ignore[attr-defined]
|
||||||
ico_buffer = self._load_ico_buffer(
|
ico_buffer = self._load_ico_buffer(
|
||||||
icon_name.replace(f"{pynicotine.__application_id__}-", "nplus-tray-"),
|
icon_name.replace(f"{pynicotine.__application_id__}-", "nplus-tray-"),
|
||||||
icon_size,
|
icon_size,
|
||||||
@@ -937,7 +946,7 @@ class Win32Implementation(BaseImplementation):
|
|||||||
|
|
||||||
with tempfile.NamedTemporaryFile(delete=False) as file_handle:
|
with tempfile.NamedTemporaryFile(delete=False) as file_handle:
|
||||||
file_handle.write(ico_buffer)
|
file_handle.write(ico_buffer)
|
||||||
return windll.user32.LoadImageA(
|
return windll.user32.LoadImageA( # type: ignore[attr-defined]
|
||||||
0,
|
0,
|
||||||
encode_path(file_handle.name),
|
encode_path(file_handle.name),
|
||||||
self.IMAGE_ICON,
|
self.IMAGE_ICON,
|
||||||
@@ -947,16 +956,15 @@ class Win32Implementation(BaseImplementation):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _destroy_h_icon(self):
|
def _destroy_h_icon(self):
|
||||||
from ctypes import windll
|
if sys.platform != "win32" or not self._h_icon:
|
||||||
|
return
|
||||||
|
|
||||||
if self._h_icon:
|
windll.user32.DestroyIcon(self._h_icon) # type: ignore[attr-defined]
|
||||||
windll.user32.DestroyIcon(self._h_icon)
|
self._h_icon = None
|
||||||
self._h_icon = None
|
|
||||||
|
|
||||||
def _update_notify_icon(self, title="", message="", icon_name=None):
|
def _update_notify_icon(self, title="", message="", icon_name=None):
|
||||||
# pylint: disable=attribute-defined-outside-init,no-member
|
# pylint: disable=attribute-defined-outside-init,no-member
|
||||||
|
if sys.platform != "win32" or self._h_wnd is None:
|
||||||
if self._h_wnd is None:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if icon_name:
|
if icon_name:
|
||||||
@@ -967,8 +975,6 @@ class Win32Implementation(BaseImplementation):
|
|||||||
# When disabled by user, temporarily show tray icon when displaying a notification
|
# When disabled by user, temporarily show tray icon when displaying a notification
|
||||||
return
|
return
|
||||||
|
|
||||||
from ctypes import byref, sizeof, windll
|
|
||||||
|
|
||||||
action = self.NIM_MODIFY
|
action = self.NIM_MODIFY
|
||||||
|
|
||||||
if self._notify_id is None:
|
if self._notify_id is None:
|
||||||
@@ -1004,23 +1010,24 @@ class Win32Implementation(BaseImplementation):
|
|||||||
ellipsize=True,
|
ellipsize=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
windll.shell32.Shell_NotifyIconW(action, byref(self._notify_id))
|
windll.shell32.Shell_NotifyIconW(action, byref(self._notify_id)) # type: ignore[attr-defined]
|
||||||
|
|
||||||
def _remove_notify_icon(self):
|
def _remove_notify_icon(self):
|
||||||
from ctypes import byref, windll
|
if sys.platform != "win32":
|
||||||
|
return
|
||||||
|
|
||||||
if self._notify_id:
|
if self._notify_id:
|
||||||
windll.shell32.Shell_NotifyIconW(self.NIM_DELETE, byref(self._notify_id))
|
windll.shell32.Shell_NotifyIconW(self.NIM_DELETE, byref(self._notify_id)) # type: ignore[attr-defined]
|
||||||
self._notify_id = None
|
self._notify_id = None
|
||||||
|
|
||||||
if self._menu:
|
if self._menu:
|
||||||
windll.user32.DestroyMenu(self._menu)
|
windll.user32.DestroyMenu(self._menu) # type: ignore[attr-defined]
|
||||||
self._menu = None
|
self._menu = None
|
||||||
|
|
||||||
def _serialize_menu_item(self, item):
|
def _serialize_menu_item(self, item):
|
||||||
# pylint: disable=attribute-defined-outside-init,no-member
|
# pylint: disable=attribute-defined-outside-init,no-member
|
||||||
|
if sys.platform != "win32":
|
||||||
from ctypes import sizeof
|
return None
|
||||||
|
|
||||||
item_info = self.MENUITEMINFOW(cb_size=sizeof(self.MENUITEMINFOW))
|
item_info = self.MENUITEMINFOW(cb_size=sizeof(self.MENUITEMINFOW))
|
||||||
w_id = item["id"]
|
w_id = item["id"]
|
||||||
@@ -1048,38 +1055,42 @@ class Win32Implementation(BaseImplementation):
|
|||||||
return item_info
|
return item_info
|
||||||
|
|
||||||
def _show_menu(self):
|
def _show_menu(self):
|
||||||
from ctypes import byref, windll, wintypes
|
if sys.platform != "win32":
|
||||||
|
return
|
||||||
|
|
||||||
|
from ctypes import wintypes # noqa: PLC0415
|
||||||
|
|
||||||
if self._menu is None:
|
if self._menu is None:
|
||||||
self.update_menu()
|
self.update_menu()
|
||||||
|
|
||||||
pos = wintypes.POINT()
|
pos = wintypes.POINT()
|
||||||
windll.user32.GetCursorPos(byref(pos))
|
windll.user32.GetCursorPos(byref(pos)) # type: ignore[attr-defined]
|
||||||
|
|
||||||
# PRB: Menus for Notification Icons Do Not Work Correctly
|
# PRB: Menus for Notification Icons Do Not Work Correctly
|
||||||
# https://web.archive.org/web/20121015064650/http://support.microsoft.com/kb/135788
|
# https://web.archive.org/web/20121015064650/http://support.microsoft.com/kb/135788
|
||||||
|
|
||||||
windll.user32.SetForegroundWindow(self._h_wnd)
|
windll.user32.SetForegroundWindow(self._h_wnd) # type: ignore[attr-defined]
|
||||||
windll.user32.TrackPopupMenu(self._menu, 0, pos.x, pos.y, 0, self._h_wnd, None)
|
windll.user32.TrackPopupMenu(self._menu, 0, pos.x, pos.y, 0, self._h_wnd, None) # type: ignore[attr-defined]
|
||||||
windll.user32.PostMessageW(self._h_wnd, self.WM_NULL, 0, 0)
|
windll.user32.PostMessageW(self._h_wnd, self.WM_NULL, 0, 0) # type: ignore[attr-defined]
|
||||||
|
|
||||||
def update_menu(self):
|
def update_menu(self):
|
||||||
from ctypes import byref, windll
|
if sys.platform != "win32":
|
||||||
|
return
|
||||||
|
|
||||||
if self._menu is None:
|
if self._menu is None:
|
||||||
self._menu = windll.user32.CreatePopupMenu()
|
self._menu = windll.user32.CreatePopupMenu() # type: ignore[attr-defined]
|
||||||
|
|
||||||
for item in self.menu_items.values():
|
for item in self.menu_items.values():
|
||||||
item_id = item["id"]
|
item_id = item["id"]
|
||||||
item_info = self._serialize_menu_item(item)
|
item_info = self._serialize_menu_item(item)
|
||||||
|
|
||||||
if not windll.user32.SetMenuItemInfoW(
|
if not windll.user32.SetMenuItemInfoW( # type: ignore[attr-defined]
|
||||||
self._menu,
|
self._menu,
|
||||||
item_id,
|
item_id,
|
||||||
False,
|
False,
|
||||||
byref(item_info),
|
byref(item_info),
|
||||||
):
|
):
|
||||||
windll.user32.InsertMenuItemW(
|
windll.user32.InsertMenuItemW( # type: ignore[attr-defined]
|
||||||
self._menu,
|
self._menu,
|
||||||
item_id,
|
item_id,
|
||||||
False,
|
False,
|
||||||
@@ -1093,7 +1104,10 @@ class Win32Implementation(BaseImplementation):
|
|||||||
self._update_notify_icon(title=title, message=message)
|
self._update_notify_icon(title=title, message=message)
|
||||||
|
|
||||||
def on_process_window_message(self, h_wnd, msg, w_param, l_param):
|
def on_process_window_message(self, h_wnd, msg, w_param, l_param):
|
||||||
from ctypes import windll, wintypes
|
if sys.platform != "win32":
|
||||||
|
return 0
|
||||||
|
|
||||||
|
from ctypes import wintypes # noqa: PLC0415
|
||||||
|
|
||||||
if msg == self.WM_TRAYICON:
|
if msg == self.WM_TRAYICON:
|
||||||
if l_param == self.WM_RBUTTONUP:
|
if l_param == self.WM_RBUTTONUP:
|
||||||
@@ -1124,7 +1138,7 @@ class Win32Implementation(BaseImplementation):
|
|||||||
self._remove_notify_icon()
|
self._remove_notify_icon()
|
||||||
self._update_notify_icon()
|
self._update_notify_icon()
|
||||||
|
|
||||||
return windll.user32.DefWindowProcW(
|
return windll.user32.DefWindowProcW( # type: ignore[attr-defined]
|
||||||
wintypes.HWND(h_wnd),
|
wintypes.HWND(h_wnd),
|
||||||
msg,
|
msg,
|
||||||
wintypes.WPARAM(w_param),
|
wintypes.WPARAM(w_param),
|
||||||
|
|||||||
@@ -65,8 +65,6 @@ class TestFlake(Flake):
|
|||||||
apply: Optional function to apply to the result
|
apply: Optional function to apply to the result
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from clan_lib.nix import nix_config
|
|
||||||
|
|
||||||
config = nix_config()
|
config = nix_config()
|
||||||
system = config["system"]
|
system = config["system"]
|
||||||
test_system = system
|
test_system = system
|
||||||
|
|||||||
@@ -66,8 +66,6 @@ lint.ignore = [
|
|||||||
"TRY301",
|
"TRY301",
|
||||||
"FBT003",
|
"FBT003",
|
||||||
"INP001",
|
"INP001",
|
||||||
# TODO: fix later
|
|
||||||
"PLC0415",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff.lint.per-file-ignores]
|
[tool.ruff.lint.per-file-ignores]
|
||||||
|
|||||||
Reference in New Issue
Block a user