Skip to main content

Driver Server client with an embeddable connector for config injection at runtime.

Project description

driverclient

Embeddable client for a Driver Server local repo. Scans a Windows machine's hardware, resolves and installs drivers from the repo, and captures drivers back up to it. Designed to be driven programmatically from a host application (for example a PyQt front-end) via a small connector class.

Install

pip install driverclient

Usage

Create one DriverClient per process and call .run():

from driverclient import DriverClient

client = DriverClient(
    local_repo_url="http://REPO_HOST:8000",
    node_key="YOUR_NODE_KEY",
)
result = client.run("capture-all")   # or client.run() for the configured default

Configuration

Config can be supplied three ways and is merged with this priority (highest wins):

DEFAULTS  <  DS_CLIENT_CONFIG env file  <  config_path file  <  keyword args
  • Args only — pass keys directly:
    DriverClient(local_repo_url="http://x:1", node_key="k")
    
  • File only — pass a JSON file used as the base:
    DriverClient(config_path="/etc/driverclient.json")
    
  • Both — file as the base, individual keys overridden by kwargs:
    DriverClient(config_path="/etc/driverclient.json", node_key="override")
    

Passing nothing preserves the default behavior (env file → packaged config.json → built-in defaults).

A dummy config.json ships inside the package as a template. Replace REPLACE_WITH_YOUR_NODE_KEY and point local_repo_url at your repo — or, better, supply real values at runtime through the connector.

Commands

scan, resolve, resolve-and-install, capture-all, capture-missing, wu-update, wu-full, automate.

client.run() with no argument uses DS_CLIENT_COMMAND, then the default_command from config.

Progress events

Every operation reports progress in real time through a structured event stream (added in 0.2.0). Pass an on_event callback to run() and it is invoked synchronously for each step:

def on_event(ev):        # ev is a driverclient.ClientEvent
    print(ev.phase, ev.status, ev.message)

client.run("automate", on_event=on_event)

When no callback is given, events fall back to print + the driverclient logger, so the terminal/log experience is unchanged.

Each ClientEvent (frozen dataclass, ev.to_dict() for transport) has:

field type meaning
phase str scan resolve install capture windows_update dump upload pipeline done
status str start progress ok warn error done
message str human-readable line (what the fallback prints)
current int | None counter position (e.g. driver 3 of 12)
total int | None counter total
percent float | None 0–100 for long single operations
data dict structured specifics (driver_name, hwid, path, error, …)
ts float time.time() at emit

phase and status are a fixed, documented vocabulary — this is the interface a GUI binds to; treat changes to it as an API change.

Events are emitted on whatever thread the op is running on — and the parallel download/export/upload pools mean some events arrive on worker threads. The Qt pattern below (queued cross-thread signals) is safe regardless.

PyQt consumer pattern

Run the (minutes-long, blocking) client.run(...) on a QThread, never the UI thread. The on_event callback must not touch widgets — it emits a Qt signal carrying event.to_dict(); a slot on the UI thread updates the widgets. Qt delivers cross-thread signals via the receiver's event loop (queued), which is the thread-safe way to drive the UI.

from PyQt6.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from driverclient import DriverClient


class Worker(QObject):
    event    = pyqtSignal(dict)     # carries ClientEvent.to_dict()
    finished = pyqtSignal(object)   # carries the op's result

    def __init__(self, command):
        super().__init__()
        self.command = command

    @pyqtSlot()
    def run(self):
        client = DriverClient(local_repo_url="http://REPO_HOST:8000",
                              node_key="YOUR_NODE_KEY")
        # Called on THIS worker thread — only emit a signal, never touch widgets.
        result = client.run(self.command, on_event=lambda ev: self.event.emit(ev.to_dict()))
        self.finished.emit(result)


class Panel:
    """Wire a worker onto a thread and bind its signals to UI-thread slots."""
    def start(self, command):
        self.thread = QThread()
        self.worker = Worker(command)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.event.connect(self.on_event)        # queued → runs on UI thread
        self.worker.finished.connect(self.on_finished)
        self.thread.start()

    @pyqtSlot(dict)
    def on_event(self, ev):
        # Safe: this runs on the UI thread. Update widgets here.
        self.status_label.setText(ev["message"])
        if ev["phase"] == "install" and ev["status"] in ("ok", "error"):
            self.step_list.addItem(ev["message"])
        if ev["total"] and ev["current"] is not None:
            self.progress_bar.setMaximum(ev["total"])
            self.progress_bar.setValue(ev["current"])
        elif ev["percent"] is not None:
            self.progress_bar.setValue(int(ev["percent"]))

    @pyqtSlot(object)
    def on_finished(self, result):
        self.thread.quit()
        self.thread.wait()

Requirements

  • Python >= 3.10
  • Windows target for the actual driver operations (pnputil, WMI, DISM).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

driverclient-0.2.0.tar.gz (28.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

driverclient-0.2.0-py3-none-any.whl (35.3 kB view details)

Uploaded Python 3

File details

Details for the file driverclient-0.2.0.tar.gz.

File metadata

  • Download URL: driverclient-0.2.0.tar.gz
  • Upload date:
  • Size: 28.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for driverclient-0.2.0.tar.gz
Algorithm Hash digest
SHA256 ec0db806f5779589ccd2b2074833e35ce67fe604048b6bf88acffac9cdd89e50
MD5 6d0e5eee67a8e92c42ccb5b5d6b562ce
BLAKE2b-256 6c5bfe63789b45c2605910980b3b22b7202ea582859961d8d185061cdd9fd691

See more details on using hashes here.

File details

Details for the file driverclient-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: driverclient-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 35.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for driverclient-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cfd9df4ddb52278c9c59567ba7cf601253a4e1d830a71fd1000974396bfad608
MD5 440abc80c99eee827aea5e7a9e25ab98
BLAKE2b-256 a98bb30843609b23e25c71d3a492c442715f9e291cda70e32a0e67a3ef906962

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page