Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ jobs:
shell: bash
run: uv run pre-commit run --all-files --show-diff-on-failure

- name: Type check with mypy
shell: bash
run: uv run mypy src/vorta/store/ src/vorta/network_status/

prepare-matrix:
runs-on: ubuntu-latest
outputs:
Expand Down
19 changes: 19 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ dev = [
"pytest-faulthandler",
"pytest-mock",
"pytest-qt",
"mypy",
"PyQt6-stubs",
"types-peewee",
"ruff",
"tox",
"twine",
Expand Down Expand Up @@ -126,6 +129,22 @@ pass_env = ["DISPLAY"]
deps = ["ruff"]
commands = [["ruff", "check", "src", "tests"]]

[tool.mypy]
python_version = "3.10"
warn_return_any = false
warn_unused_configs = true
disallow_untyped_defs = false
check_untyped_defs = false
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = ["vorta.store.models", "vorta.store.connection", "vorta.store.settings", "vorta.network_status.*"]
disallow_untyped_defs = true

[[tool.mypy.overrides]]
module = ["vorta.store.migrations"]
ignore_errors = true

[tool.pylint.master]
extension-pkg-whitelist = "PyQt6"
load-plugins = ""
Expand Down
26 changes: 14 additions & 12 deletions src/vorta/network_status/abc.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from __future__ import annotations

import sys
from datetime import datetime
from typing import List, NamedTuple, Optional
from typing import NamedTuple

from PyQt6.QtCore import QObject, pyqtSignal


class NetworkStatusMonitor(QObject):
@classmethod
def get_network_status_monitor(cls) -> 'NetworkStatusMonitor':
def get_network_status_monitor(cls) -> NetworkStatusMonitor:
if sys.platform == 'darwin':
from .darwin import DarwinNetworkStatus

Expand All @@ -26,10 +28,10 @@ def get_network_status_monitor(cls) -> 'NetworkStatusMonitor':

network_status_changed = pyqtSignal(bool, name="networkStatusChanged")

def __init__(self, parent=None):
def __init__(self, parent: QObject | None = None) -> None:
super().__init__(parent)

def is_network_status_available(self):
def is_network_status_available(self) -> bool:
"""Is the network status really available, and not just a dummy implementation?"""
return type(self) is not NetworkStatusMonitor

Expand All @@ -44,37 +46,37 @@ def is_network_metered(self) -> bool:
"""Is the currently connected network a metered connection?"""
raise NotImplementedError()

def get_current_wifi(self) -> Optional[str]:
def get_current_wifi(self) -> str | None:
"""Get current SSID or None if Wifi is off."""
raise NotImplementedError()

def get_known_wifis(self) -> List['SystemWifiInfo']:
def get_known_wifis(self) -> list[SystemWifiInfo]:
"""Get WiFi networks known to system."""
raise NotImplementedError()


class SystemWifiInfo(NamedTuple):
ssid: str
last_connected: Optional[datetime]
last_connected: datetime | None


class NullNetworkStatusMonitor(NetworkStatusMonitor):
"""Dummy implementation, in case we don't have one for current platform."""

def __init__(self):
def __init__(self) -> None:
super().__init__()

def is_network_active(self):
def is_network_active(self) -> bool:
return True

def is_network_status_available(self):
def is_network_status_available(self) -> bool:
return False

def is_network_metered(self) -> bool:
return False

def get_current_wifi(self) -> Optional[str]:
def get_current_wifi(self) -> str | None:
pass

def get_known_wifis(self) -> List['SystemWifiInfo']:
def get_known_wifis(self) -> list[SystemWifiInfo]:
return []
32 changes: 17 additions & 15 deletions src/vorta/network_status/darwin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

import subprocess
from collections.abc import Iterator
from datetime import datetime as dt
from typing import Iterator, List, Optional

from CoreWLAN import CWInterface, CWNetwork, CWWiFiClient

Expand All @@ -9,7 +11,7 @@


class DarwinNetworkStatus(NetworkStatusMonitor):
def __init__(self):
def __init__(self) -> None:
super().__init__()

def is_network_metered(self) -> bool:
Expand All @@ -19,7 +21,7 @@ def is_network_metered(self) -> bool:
if interface is None:
return False

network: Optional[CWNetwork] = interface.lastNetworkJoined()
network: CWNetwork | None = interface.lastNetworkJoined()

if network:
is_ios_hotspot = network.isPersonalHotspot()
Expand All @@ -28,34 +30,34 @@ def is_network_metered(self) -> bool:

return is_ios_hotspot or any(is_network_metered_with_android(d) for d in get_network_devices())

def is_network_active(self):
def is_network_active(self) -> bool:
# Not yet implemented
return True

def get_current_wifi(self) -> Optional[str]:
def get_current_wifi(self) -> str | None:
"""
Get current SSID or None if Wi-Fi is off.
"""
interface: Optional[CWInterface] = self._get_wifi_interface()
interface: CWInterface | None = self._get_wifi_interface()
if not interface:
return None

# If the user has Wi-Fi turned off lastNetworkJoined will return None.
network: Optional[CWNetwork] = interface.lastNetworkJoined()
network: CWNetwork | None = interface.lastNetworkJoined()

if network:
network_name = network.ssid()
return network_name
else:
return None

def get_known_wifis(self) -> List[SystemWifiInfo]:
def get_known_wifis(self) -> list[SystemWifiInfo]:
"""
Use the program, "networksetup", to get the list of know Wi-Fi networks.
"""

wifis = []
interface: Optional[CWInterface] = self._get_wifi_interface()
interface: CWInterface | None = self._get_wifi_interface()
if not interface:
return []

Expand All @@ -76,9 +78,9 @@ def get_known_wifis(self) -> List[SystemWifiInfo]:

return wifis

def _get_wifi_interface(self) -> Optional[CWInterface]:
def _get_wifi_interface(self) -> CWInterface | None:
wifi_client: CWWiFiClient = CWWiFiClient.sharedWiFiClient()
interface: Optional[CWInterface] = wifi_client.interface()
interface: CWInterface | None = wifi_client.interface()
return interface


Expand All @@ -88,11 +90,11 @@ def get_network_devices() -> Iterator[str]:
yield line.split()[1].strip().decode('ascii')


def is_network_metered_with_android(bsd_device) -> bool:
def is_network_metered_with_android(bsd_device: str) -> bool:
return b'ANDROID_METERED' in call_ipconfig_getpacket(bsd_device)


def call_ipconfig_getpacket(bsd_device):
def call_ipconfig_getpacket(bsd_device: str) -> bytes:
cmd = ['ipconfig', 'getpacket', bsd_device]
try:
return subprocess.check_output(cmd)
Expand All @@ -101,7 +103,7 @@ def call_ipconfig_getpacket(bsd_device):
return b''


def call_networksetup_listallhardwareports():
def call_networksetup_listallhardwareports() -> bytes:
cmd = ['/usr/sbin/networksetup', '-listallhardwareports']
try:
return subprocess.check_output(cmd)
Expand All @@ -110,7 +112,7 @@ def call_networksetup_listallhardwareports():
return b''


def call_networksetup_listpreferredwirelessnetworks(interface) -> str:
def call_networksetup_listpreferredwirelessnetworks(interface: str) -> str:
command = ['/usr/sbin/networksetup', '-listpreferredwirelessnetworks', interface]
try:
return subprocess.check_output(command).decode(encoding='utf-8')
Expand Down
Loading
Loading