driverclient 0.2.28__tar.gz → 0.2.30__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {driverclient-0.2.28 → driverclient-0.2.30}/PKG-INFO +1 -1
  2. {driverclient-0.2.28 → driverclient-0.2.30}/pyproject.toml +1 -1
  3. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/__init__.py +1 -1
  4. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/ops/capture.py +3 -15
  5. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/ops/download.py +19 -1
  6. {driverclient-0.2.28 → driverclient-0.2.30}/.gitignore +0 -0
  7. {driverclient-0.2.28 → driverclient-0.2.30}/README.md +0 -0
  8. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/__main__.py +0 -0
  9. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/config.json +0 -0
  10. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/config.py +0 -0
  11. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/core/__init__.py +0 -0
  12. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/core/hardware.py +0 -0
  13. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/core/http.py +0 -0
  14. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/core/hwid.py +0 -0
  15. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/core/proc.py +0 -0
  16. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/events.py +0 -0
  17. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/main.py +0 -0
  18. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/ops/__init__.py +0 -0
  19. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/ops/automate.py +0 -0
  20. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/ops/diagnose.py +0 -0
  21. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/ops/install.py +0 -0
  22. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/ops/offline.py +0 -0
  23. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/ops/resolve.py +0 -0
  24. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/ops/scan.py +0 -0
  25. {driverclient-0.2.28 → driverclient-0.2.30}/src/driverclient/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: driverclient
3
- Version: 0.2.28
3
+ Version: 0.2.30
4
4
  Summary: Driver Server client with an embeddable connector for config injection at runtime.
5
5
  Project-URL: Homepage, https://example.com/driverclient
6
6
  Author-email: Raja Sanaullah <sanaullah@99technologies.com>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "driverclient"
7
- version = "0.2.28"
7
+ version = "0.2.30"
8
8
  description = "Driver Server client with an embeddable connector for config injection at runtime."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -26,7 +26,7 @@ from driverclient.main import run as _run
26
26
 
27
27
  __all__ = ["ClientEvent", "DriverClient"]
28
28
 
29
- __version__ = "0.2.28"
29
+ __version__ = "0.2.30"
30
30
 
31
31
 
32
32
  class DriverClient:
@@ -404,22 +404,12 @@ def wu_full() -> CaptureResult:
404
404
 
405
405
  # ══ Missing-HWID classification ═══════════════════════════════════════════════
406
406
 
407
- _INBOX_INADEQUATE_CLASSES = {"display adapters"}
408
-
409
407
 
410
408
  def classify_missing(not_found: list[str], scan_result: ScanResult) -> dict[str, list[str]]:
411
409
  """Bucket repo-missing HWIDs by what the machine says about their device:
412
410
  inbox (Windows ships the driver — not applicable), oem_local (OEM driver
413
411
  installed locally — capturable), driverless (genuinely missing — WU
414
- candidate), unknown (HWID not in scan).
415
-
416
- Display adapters are excluded from the inbox bucket: Microsoft Basic
417
- Display Adapter is Windows' inbox fallback for a GPU whose real driver
418
- was never bound, and it leaves the device non-accelerated — unlike other
419
- inbox drivers (HID, mass storage, ...) it is not adequate coverage. Such
420
- a device is treated as driverless so it still reaches WU / gets captured
421
- once a real driver shows up, instead of being silently written off.
422
- """
412
+ candidate), unknown (HWID not in scan)."""
423
413
  hwid_to_device: dict[str, Device] = {}
424
414
  for scanned in scan_result.devices:
425
415
  for h in scanned.hwids:
@@ -437,10 +427,8 @@ def classify_missing(not_found: list[str], scan_result: ScanResult) -> dict[str,
437
427
  buckets["unknown"].append(hwid)
438
428
  elif not dev.has_driver:
439
429
  buckets["driverless"].append(hwid)
440
- elif dev.is_inbox and dev.class_name.lower() not in _INBOX_INADEQUATE_CLASSES:
441
- buckets["inbox"].append(hwid)
442
430
  elif dev.is_inbox:
443
- buckets["driverless"].append(hwid)
431
+ buckets["inbox"].append(hwid)
444
432
  else:
445
433
  buckets["oem_local"].append(hwid)
446
434
  return buckets
@@ -1214,4 +1202,4 @@ def _upload_worker(upload_q: queue.Queue, counts: dict, lock: threading.Lock, cf
1214
1202
  # Extension selection was removed: capture now packs every file
1215
1203
  # pnputil /export-driver produces (see _do_export), so the dump_extensions /
1216
1204
  # dump_extensions_overrides config keys are no longer consulted — filtering by
1217
- # extension is what caused incomplete packages (broken link D).
1205
+ # extension is what caused incomplete packages (broken link D).
@@ -12,6 +12,7 @@ Factored out here so the two modes share one download implementation:
12
12
  • download_drivers() — fan the above out across a driver list in parallel
13
13
  """
14
14
 
15
+ import shutil
15
16
  from concurrent.futures import ThreadPoolExecutor, as_completed
16
17
  from pathlib import Path
17
18
 
@@ -20,7 +21,21 @@ from driverclient.core.http import http_download_retry
20
21
 
21
22
  def download_driver(driver: dict, base: Path, repo_url: str, cfg: dict) -> tuple[Path, bool]:
22
23
  """Download all files for one driver into base/<binding_id>/.
23
- Returns (dest_dir, success). success is False if ANY file failed."""
24
+ Returns (dest_dir, success). success is False if ANY file failed.
25
+
26
+ On failure, dest is removed before returning. The live install path stages
27
+ every driver under one shared `base` and installs it with a single batched
28
+ `pnputil /add-driver base\\*.inf /subdirs` — that command globs the
29
+ filesystem directly, so a driver this function reports as failed (and the
30
+ caller correctly excludes from the install list) would otherwise still
31
+ leave its already-downloaded files (commonly just the small .inf, if a
32
+ large companion .sys/.dll timed out) sitting under base/<binding_id>/ for
33
+ pnputil to find anyway. pnputil then tries to add that incomplete package —
34
+ "cannot find the file specified" for its missing payload — which large
35
+ graphics drivers hit most often (biggest files, likeliest to time out
36
+ partway), and can poison the whole batched pass for the OTHER, fully
37
+ downloaded drivers riding in the same pnputil call.
38
+ """
24
39
  bid = driver.get("binding_id") or driver.get("package_id", "")
25
40
  pkg_id = driver["package_id"]
26
41
  dest = base / bid
@@ -36,6 +51,9 @@ def download_driver(driver: dict, base: Path, repo_url: str, cfg: dict) -> tuple
36
51
  if not http_download_retry(url, path, f["sha256"], timeout=cfg["timeout_long"]):
37
52
  ok = False
38
53
 
54
+ if not ok:
55
+ shutil.rmtree(dest, ignore_errors=True)
56
+
39
57
  return dest, ok
40
58
 
41
59
 
File without changes
File without changes