Skip to content
Draft
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
1 change: 0 additions & 1 deletion fetch_cord/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# Timing constants (in seconds)
MIN_CYCLE_TIME_SECONDS = 15
DEFAULT_CYCLE_TIME_SECONDS = 30
WAIT_INTERVAL_SECONDS = 0.05

# Git branches
DEFAULT_BRANCH = "master"
Expand Down
11 changes: 2 additions & 9 deletions fetch_cord/cycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from fetch_cord.constants import (
DEFAULT_CYCLE_TIME_SECONDS,
RPC_CONNECTION_REFUSED_MSG,
WAIT_INTERVAL_SECONDS,
)
from fetch_cord.presence import build_presence_activity

Expand Down Expand Up @@ -118,14 +117,8 @@ def update(
self.close_connection()
raise

def wait(self, n: float, interval_duration: float = WAIT_INTERVAL_SECONDS) -> None:
"""Wait for n seconds, returning early if we've been told to stop.

``interval_duration`` is kept for backwards compatibility and ignored:
Event.wait already returns the moment the event is set, so polling it
in short slices only costs wakeups.
"""
del interval_duration
def wait(self, n: float) -> None:
"""Wait for n seconds, returning early if we've been told to stop."""
self.stop.wait(n)

def __repr__(self) -> str:
Expand Down
23 changes: 3 additions & 20 deletions fetch_cord/fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Protocol

from fetch_cord import resources
from fetch_cord.constants import RESULT_NOT_FOUND, UNKNOWN_COMPONENT_ID
from fetch_cord.constants import UNKNOWN_COMPONENT_ID
from fetch_cord.info import FASTFETCH_MODULES, parse_fastfetch_json
from fetch_cord.native import native as native_module
from fetch_cord.tools import BashError, exec_bash, exec_ps1, run_command
Expand Down Expand Up @@ -70,8 +70,6 @@ class FieldProvider(Protocol):
"""A source of system-info fields. Later providers only fill gaps left by
earlier ones, so adding a provider (e.g. for GPU/LLM) is additive."""

name: str

def fetch(self, skip: set[str] | None = None) -> dict[str, str]: ...


Expand All @@ -80,8 +78,6 @@ class FastfetchProvider:
fields. Returns {} if fastfetch is not installed, so the command provider
below can take over (e.g. on Windows)."""

name = "fastfetch"

def __init__(self, binary: str = "fastfetch") -> None:
self.binary = binary

Expand Down Expand Up @@ -115,8 +111,6 @@ class CommandProvider:
"""Runs per-OS shell commands for fields fastfetch doesn't cover (e.g.
motherboard, resolution, system_type) or when fastfetch is unavailable."""

name = "commands"

def __init__(self, commands: dict[str, str]) -> None:
# commands: {field: shell_command}, already filtered for this OS.
self.commands = commands
Expand Down Expand Up @@ -144,7 +138,6 @@ class NativeProvider:
Win32 directly. Adds no tool dependency and no subprocess, and returns {}
for anything it cannot read so another provider fills the gap."""

name = "native"
NATIVE_COMPONENTS = (
"cpu",
"host",
Expand All @@ -170,15 +163,13 @@ def fetch(self, skip: set[str] | None = None) -> dict[str, str]:


class Fetch:
"""Collects system info from a chain of providers and caches the result.
"""Collects system info from a chain of providers.

Call `snapshot()` once per refresh to collect every field, then read
individual values with `fetch(field)` from the cached snapshot.
Call `snapshot()` once per refresh to collect every field.
"""

def __init__(self, providers: list[FieldProvider]) -> None:
self.providers = providers
self._cache: dict[str, str] = {}

def snapshot(self) -> dict[str, str]:
data: dict[str, str] = {}
Expand All @@ -188,12 +179,4 @@ def snapshot(self) -> dict[str, str]:
# Hardware cycle reads `mem`; fastfetch emits `memory`.
if "memory" in data and "mem" not in data:
data["mem"] = data["memory"]
self._cache = data
return data

def fetch(self, field: str | None) -> str:
if field is None:
return RESULT_NOT_FOUND
if field not in self._cache:
self.snapshot()
return self._cache.get(field, RESULT_NOT_FOUND)
3 changes: 0 additions & 3 deletions fetch_cord/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ def _format_module(module_type: str, result: dict[str, Any]) -> str:
if module_type == "CPU":
return result.get("cpu") or result.get("name") or _first_text(result)

if module_type == "GPU":
return _format_gpu(result)

if module_type == "Memory":
used = int(result.get("used") or 0)
total = int(result.get("total") or 0)
Expand Down
44 changes: 0 additions & 44 deletions fetch_cord/resources/desktop.json

This file was deleted.

9 changes: 0 additions & 9 deletions fetch_cord/resources/fetchcord_cmds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,6 @@ commands:
if [ -f /etc/os-release ]; then . /etc/os-release; echo "$PRETTY_NAME"; else uname -s; fi
Darwin: |
echo "$(sw_vers -productName) $(sw_vers -productVersion)"
host:
Windows: |
Get-WmiObject Win32_BaseBoard | Select-Object Product | ForEach-Object {$_.Product}
Linux: |
cat /sys/devices/virtual/dmi/id/product_name 2>/dev/null
Darwin: |
sysctl -n hw.model
motherboard:
# Manufacturer on its own is just a vendor ("ASUSTeK COMPUTER INC."),
# which is all the host cycle had to show and all the id table had to
Expand Down Expand Up @@ -158,8 +151,6 @@ commands:
}
Linux: |
fc-match monospace 2>/dev/null | sed 's/:.*//' || true
Darwin: |
true
terminal:
Windows: |
$host.Name
Expand Down
32 changes: 0 additions & 32 deletions fetch_cord/resources/windowmanager.json

This file was deleted.

4 changes: 1 addition & 3 deletions fetch_cord/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
COMMAND_TIMEOUT_SECONDS = 60


def run_command(command: list[str], shell: bool = False) -> str:
def run_command(command: list[str]) -> str:
try:
result = subprocess.run(
command,
Expand All @@ -22,8 +22,6 @@ def run_command(command: list[str], shell: bool = False) -> str:
# Otherwise a helper's warnings get printed over our own output.
stderr=subprocess.DEVNULL,
timeout=COMMAND_TIMEOUT_SECONDS,
# trunk-ignore(bandit/B602)
shell=shell,
)
except subprocess.TimeoutExpired as exc:
raise BashError(f"Command timed out after {exc.timeout}s: {command}") from exc
Expand Down
67 changes: 0 additions & 67 deletions tests/test_fetch_cord.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,25 +228,6 @@ def test_cycle_wait(self, mock_presence_class):
stop_event.set()
cycle.wait(0.1)

@patch("fetch_cord.cycle.Presence")
def test_cycle_wait_with_interval(self, mock_presence_class):
"""Test Cycle wait method with interval checking."""
from fetch_cord.cycle import Cycle

stop_event = Event()
cycle = Cycle(self.config, stop_event)

def set_stop():
stop_event.set()

import threading

timer = threading.Timer(0.05, set_stop)
timer.start()

cycle.wait(1.0, interval_duration=0.01)
timer.join()

@patch("fetch_cord.cycle.Presence")
def test_cycle_wait_returns_promptly_when_stopped(self, mock_presence_class):
"""A 30s cycle must not keep the process alive for 30s after Ctrl+C."""
Expand Down Expand Up @@ -527,14 +508,10 @@ def test_fetch_snapshot_first_provider_wins(self):
from fetch_cord.fetch import Fetch

class ProviderOne:
name = "one"

def fetch(self, skip=None):
return {"a": "1", "b": "1"}

class ProviderTwo:
name = "two"

def fetch(self, skip=None):
# Only fills gaps; must not overwrite existing fields.
return {"b": "2", "c": "2"}
Expand All @@ -543,29 +520,11 @@ def fetch(self, skip=None):
snap = fetch.snapshot()

self.assertEqual(snap, {"a": "1", "b": "1", "c": "2"})
self.assertEqual(fetch.fetch("a"), "1")
self.assertEqual(fetch.fetch("b"), "1")
self.assertEqual(fetch.fetch("c"), "2")

def test_fetch_missing_field_returns_not_found(self):
from fetch_cord.fetch import Fetch

class EmptyProvider:
name = "empty"

def fetch(self, skip=None):
return {}

fetch = Fetch([EmptyProvider()])
self.assertEqual(fetch.fetch("nonexistent"), "Not Found")
self.assertEqual(fetch.fetch(None), "Not Found")

def test_snapshot_aliases_memory_to_mem(self):
from fetch_cord.fetch import Fetch

class MemProvider:
name = "mem"

def fetch(self, skip=None):
return {"memory": "2.00 GB / 4.00 GB"}

Expand Down Expand Up @@ -611,31 +570,9 @@ def test_run_command(self, mock_run):
stdout=-1,
stderr=subprocess.DEVNULL,
timeout=COMMAND_TIMEOUT_SECONDS,
shell=False,
)
self.assertEqual(result, "command output")

@patch("fetch_cord.tools.subprocess.run")
def test_run_command_with_shell(self, mock_run):
"""Test run_command with shell=True."""
mock_process = MagicMock()
mock_process.stdout = "shell output"
mock_run.return_value = mock_process

from fetch_cord.tools import run_command

run_command(["echo", "hello"], shell=True)

mock_run.assert_called_once_with(
["echo", "hello"],
encoding="utf-8",
errors="replace",
stdout=-1,
stderr=subprocess.DEVNULL,
timeout=COMMAND_TIMEOUT_SECONDS,
shell=True,
)

@patch("fetch_cord.tools.subprocess.run")
def test_exec_bash(self, mock_run):
"""Test exec_bash function."""
Expand Down Expand Up @@ -826,14 +763,10 @@ def test_fetch_chain_fills_gaps(self):
from fetch_cord.fetch import Fetch

class FastfetchFake:
name = "fastfetch"

def fetch(self, skip=None):
return {"os": "Debian GNU/Linux", "kernel": "6.8.0"}

class CommandFake:
name = "commands"

def fetch(self, skip=None):
return {"system_type": "Desktop"}

Expand Down
Loading