Read-only Wi-Fi checker workflow: inspect, run safely, handle errors and test.

Python Wi-Fi Diagnostics on Windows: A Read-Only Mini-Project

Practical guides / Python projects

A Windows laptop says it is connected to Wi-Fi, yet a page will not load. Before changing drivers or resetting the network, inspect what Windows actually reports. This small Python project runs one read-only command and makes its output available in your terminal.

You will build a Wi-Fi interface checker, not a password viewer. It does not request saved keys, reconnect the adapter or change network settings. That narrower purpose makes it a useful first exercise in operating-system automation: run a fixed command, handle failure, then explain what the evidence does and does not prove.

Build: a local command-line report. Level: Python beginner with basic functions. Needs: Python 3.10 or newer; Windows for live results. No extra packages: the example uses the standard library.

Start safely: the demo runs without querying Wi-Fi. Use live mode only on a computer you own or are allowed to troubleshoot.

First separate a Wi-Fi problem from a Python problem

Open a normal Windows Terminal or PowerShell window. Start with this command, without Python:

netsh wlan show interfaces

Microsoft documents this as a way to display wireless interface information. A machine with a working wireless adapter may return its connection state, network name, signal and other fields. Labels and available details vary with Windows, language, hardware, drivers and permissions.

If Windows says the WLAN service is unavailable or the command is unknown, a Python wrapper cannot repair that. Keep the original message. On a server without the wireless networking components, this project may have no live Wi-Fi information to show.

A connected state describes the radio connection, not the entire route to the internet. It does not prove that DNS works, that a remote service is reachable or that a particular website is healthy. Keep those separate while diagnosing a failed request.

The project has a small, deliberate boundary

  1. Locate the Windows copy of netsh.exe in the system directory.
  2. Run the fixed arguments wlan show interfaces without a command shell.
  3. Display the returned message and preserve failure as a nonzero program exit.
  4. Leave the adapter and stored network credentials alone.

There is no network-name input box. That removes the need to insert user input into a command. The program also leaves the report in Windows’ own wording instead of pretending that English field labels are a portable data format.

Create the Python checker

Make an empty project folder and save the following file as wifi_check.py. Do not name it subprocess.py or ctypes.py, because those names would shadow the modules that the example imports.

"""Read local Wi-Fi interface information. Never request saved credentials."""
import argparse
import ctypes
import os
from pathlib import Path
import subprocess
import sys

DEMO = """DEMO DATA, not a scan of this computer
Name : Wi-Fi
State : connected
SSID : ExampleHome
Signal : 78%
"""


def display_text(value):
    """Remove terminal escape/control characters without changing line breaks."""
    return "".join(c for c in value if c.isprintable() or c in "\n\t")


def read_interfaces(executable, encoding, runner=subprocess.run):
    result = runner(
        [str(executable), "wlan", "show", "interfaces"],
        shell=False,
        capture_output=True,
        timeout=10,
        check=False,
    )
    stdout = result.stdout.decode(encoding, errors="replace")
    stderr = result.stderr.decode(encoding, errors="replace")
    return result.returncode, display_text(stdout), display_text(stderr)


def main(argv=None):
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--demo", action="store_true", help="Use fictional output")
    args = parser.parse_args(argv)
    if args.demo:
        print(DEMO, end="")
        return 0
    if sys.platform != "win32":
        print("Live mode needs Windows. Try --demo.", file=sys.stderr)
        return 2
    root = os.environ.get("SystemRoot")
    if not root or not Path(root).is_absolute():
        print("Windows system directory is unavailable.", file=sys.stderr)
        return 2
    executable = Path(root) / "System32" / "netsh.exe"
    if not executable.is_file():
        print("The Windows netsh executable was not found.", file=sys.stderr)
        return 2
    try:
        encoding = "cp" + str(ctypes.windll.kernel32.GetOEMCP())
        code, output, error = read_interfaces(executable, encoding)
    except subprocess.TimeoutExpired:
        print("Wi-Fi query timed out. No settings were changed.", file=sys.stderr)
        return 3
    except (OSError, LookupError) as exc:
        print("Wi-Fi query could not run: " + str(exc), file=sys.stderr)
        return 3
    print("Local Wi-Fi interface report. Review before sharing.")
    if output.strip():
        print(output.rstrip())
    if error.strip():
        print(error.rstrip(), file=sys.stderr)
    if code:
        print("Windows returned an error. Read its message above.", file=sys.stderr)
        return 1
    if not output.strip() and not error.strip():
        print("Windows returned no interface details.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Why these details are in the example

subprocess.run receives a list of separate arguments and shell=False. It launches the program directly rather than passing a constructed command string to a shell. A fixed command is also easier to inspect in a test.

The ten-second timeout bounds the wait for the child process after it starts; it is not a promise that every operating-system process launch finishes within ten seconds. Python documents that initial process creation can have platform-specific delays.

The command returns bytes. The example asks Windows for its OEM code page, then decodes the text with replacement for invalid byte sequences. This avoids a decoding crash on unusual output, although replacement characters mean that some text could not be recovered exactly. If a network name looks wrong, compare it with the original Windows command rather than trusting a damaged rendering.

display_text removes control characters such as the terminal escape character before printing. It retains line breaks and tabs. This is a display safeguard, not anonymization: a report may still contain identifying network names or addresses.

The executable path comes from the Windows system directory rather than a matching filename in the project folder. This assumes an ordinary trusted Windows environment. It does not make a compromised computer safe, and you should not run downloaded executables with the same name to get around a missing system component.

Run the demo, then query your own computer

Open a terminal in the project folder. Confirm that your interpreter is Python 3.10 or newer, then run the demo:

python --version
python wifi_check.py --demo

If your Windows installation uses the Python launcher instead, use py in place of python. The demo prints this fictional example:

DEMO DATA, not a scan of this computer
Name : Wi-Fi
State : connected
SSID : ExampleHome
Signal : 78%

The number is sample data, not a reading from your adapter. Demo mode returns before the Windows query, so it also works on a development machine with no Wi-Fi interface.

For a real local report on Windows, omit the flag:

python wifi_check.py

Read the original Windows message below the report heading. If it describes a disconnected interface, you have evidence about the current connection state. If it reports no wireless interface, first check whether the machine actually has one. Do not turn an absence of output into a claim that the network is secure or healthy.

In PowerShell, check the program’s exit code immediately afterwards:

$LASTEXITCODE
  • 0: demo completed or the Windows command returned success. This does not prove internet access.
  • 1: Windows returned a command error. Read its message.
  • 2: live mode cannot start in this environment, or the command-line arguments are invalid.
  • 3: execution timed out or the program could not run or decode the query.

Keep the report private until you have reviewed it

The script does not upload the report or save a file. That does not make the output anonymous. Before sharing it, remove network names, hardware identifiers and other details that identify a person, workplace or location. If you paste it into an AI tool for help, share the smallest relevant error message instead of the whole report.

This is not a credential recovery tool, a network scanner or an access-control bypass. On a managed device, follow the administrator’s troubleshooting process. Do not disable security controls or elevate the terminal simply because a tutorial did not anticipate your configuration.

Test the failure paths without disconnecting anything

You do not need to break your Wi-Fi connection to check the error handling. Save this second file beside wifi_check.py. It replaces the command runner with small test doubles; none of these tests query or reconfigure an adapter.

import contextlib
import io
import subprocess
import unittest
from unittest.mock import patch
import wifi_check


class WifiCheckTests(unittest.TestCase):
    def test_fixed_read_only_command_and_timeout(self):
        def fake(command, **options):
            self.assertEqual(command, ["netsh.exe", "wlan", "show", "interfaces"])
            self.assertFalse(options["shell"])
            self.assertEqual(options["timeout"], 10)
            self.assertTrue(options["capture_output"])
            return subprocess.CompletedProcess(command, 0, b"State : connected\r\n", b"")
        code, output, error = wifi_check.read_interfaces("netsh.exe", "utf-8", fake)
        self.assertEqual((code, output, error), (0, "State : connected\n", ""))

    def test_nonzero_exit_and_stderr_are_preserved(self):
        fake = lambda *a, **k: subprocess.CompletedProcess(a[0], 5, b"", b"Access denied")
        self.assertEqual(wifi_check.read_interfaces("netsh.exe", "utf-8", fake),
                         (5, "", "Access denied"))

    def test_localized_text_is_not_parsed_as_english(self):
        text = "Estado : conectado\nSe\u00f1al : 78%\n"
        fake = lambda *a, **k: subprocess.CompletedProcess(a[0], 0, text.encode("cp850"), b"")
        self.assertEqual(wifi_check.read_interfaces("netsh.exe", "cp850", fake)[1], text)

    def test_invalid_bytes_do_not_crash(self):
        fake = lambda *a, **k: subprocess.CompletedProcess(a[0], 0, b"SSID : \xff", b"")
        self.assertIn("\ufffd", wifi_check.read_interfaces("netsh.exe", "utf-8", fake)[1])

    def test_timeout_propagates_to_main(self):
        def fake(*a, **k):
            raise subprocess.TimeoutExpired(a[0], 10)
        with self.assertRaises(subprocess.TimeoutExpired):
            wifi_check.read_interfaces("netsh.exe", "utf-8", fake)

    def test_control_characters_removed(self):
        self.assertEqual(wifi_check.display_text("ok\x1b\x00\r\nnext\tline"), "ok\nnext\tline")

    def test_demo_never_queries_windows(self):
        stream = io.StringIO()
        with patch.object(wifi_check, "read_interfaces", side_effect=AssertionError("Live query")):
            with contextlib.redirect_stdout(stream):
                self.assertEqual(wifi_check.main(["--demo"]), 0)
        self.assertIn("DEMO DATA", stream.getvalue())

    def test_non_windows_returns_actionable_error(self):
        stream = io.StringIO()
        with patch.object(wifi_check.sys, "platform", "linux"):
            with contextlib.redirect_stderr(stream):
                self.assertEqual(wifi_check.main([]), 2)
        self.assertIn("--demo", stream.getvalue())


if __name__ == "__main__":
    unittest.main()

Run the test file from that folder:

python -m unittest -v test_wifi_check.py

The suite contains eight test methods. They check fixed command arguments, timeout propagation, error output, localized text, invalid bytes, control-character handling, the demo boundary and the non-Windows guard. These are software checks, not a claim of compatibility with every adapter or Windows build.

Verification for this article, 26 October 2026: all eight tests and the demo were run successfully on the development host. A real read-only invocation was also attempted. That host returned “The following command was not found: wlan show interfaces.” The script reported the failure and exited with code 1. A successful live wireless-adapter report was not tested on that host.

For a broader testing method, follow the practical regression workflow for AI-generated code. It shows why a plausible-looking fix still needs checks that would reject a wrong alternative.

Troubleshoot the message you actually received

“Python is not recognized”

The interpreter did not start. Check your Python installation or try py --version on Windows. Keep that separate from the Wi-Fi command: there is no reason to edit a network setting to fix a missing interpreter.

The Windows command is missing or reports a service problem

Run netsh wlan show interfaces directly once and compare the message. A server image, missing wireless components, driver state or organization policy can explain why the command is unavailable. Use Windows Settings or ask the administrator to inspect the supported configuration. The tutorial deliberately does not install features or start services.

The name contains replacement characters

Byte decoding lost information. Check the original command in your Windows terminal and avoid adding a parser based on the corrupted string. The test with Spanish text exercises a decoding path; it does not certify every Windows locale.

The report says connected, but a website still fails

Write down the narrower observation: Windows reports an association with a wireless network. Next distinguish an application failure from a DNS or wider connectivity problem using your normal support process. Repeatedly running this same query will not tell you whether a remote service is down.

Extend the project without turning it into a secret collector

Add a --summary option that reports only whether the command ran successfully, without storing its output. Test the success, nonzero-exit and timeout cases before using it. Keep the fixed command and avoid exposing arbitrary command arguments through a web form.

For a portfolio, include the source, tests and a short README describing the boundary: it inspects one local interface report and does not diagnose the entire network. Use demo output in a public screenshot. A precise limitation is more credible than promising that a small script can fix every connection problem.

Need the foundations first? Continue through the Python programming learning path. If your actual task is an Android device rather than a Windows adapter, use the separate wireless ADB guide; it serves a different purpose.

Official references and review notes

This is an original implementation and explanation. The sources below document the operating-system command and Python behaviour used in the project:

Reviewed on 26 October 2026. Recheck command availability and output handling when you change the operating system or interpreter. Choose another practical guide.

Similar Posts