blog Exploring Python’s webbrowser.open

Tags:
python vscode

Python has a webbrowser module that allows scripts to open a webbrowser. In the past, I have often used this for scripts that create some kind of new web resource, so that it will automatically open the page after creation. While working on one of my headless servers, I became curious about how it works and how I might be able to tap into it.

The interface for the module is quite simple.

import webbrowser
webbrowser.open("http://example.com")

and it can also be called as a module

python -m webbrowser http://example.com

Looking at some of the main parts, the main core is roughly layed out in the open() method.

def open(url, new=0, autoraise=True):
    register_standard_browsers()
    for name in _tryorder:
        browser = get(name)
        if browser.open(url, new, autoraise):
            return True
    return False

if we look at register_standard_browsers(), we can see this basic structure.

def register_standard_browsers():
    if sys.platform == 'darwin':
        register("MacOSX", None, MacOSXOSAScript('default'))
        register("chrome", None, MacOSXOSAScript('chrome'))
        register("firefox", None, MacOSXOSAScript('firefox'))
        register("safari", None, MacOSXOSAScript('safari'))
    if sys.platform[:3] == "win":
        register("windows-default", WindowsDefault)
    else:
        if os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"):
            register_X_browsers()
        # Also try console browsers
        if os.environ.get("TERM"):
            # Common symbolic link for the default text-based browser
            if shutil.which("www-browser"):
                register("www-browser", None, GenericBrowser("www-browser"))
            # The Links/elinks browsers <http://links.twibright.com/>
            if shutil.which("links"):
                register("links", None, GenericBrowser("links"))
            if shutil.which("elinks"):
                register("elinks", None, Elinks("elinks"))
            # The Lynx browser <https://lynx.invisible-island.net/>, <http://lynx.browser.org/>
            if shutil.which("lynx"):
                register("lynx", None, GenericBrowser("lynx"))
            # The w3m browser <http://w3m.sourceforge.net/>
            if shutil.which("w3m"):
                register("w3m", None, GenericBrowser("w3m"))

    if "BROWSER" in os.environ:
        for cmdline in os.environ["BROWSER"].split(os.pathsep):
            register(cmdline, None, GenericBrowser(cmdline), preferred=True)

There is a lot more error checking in here, but the main logic is all here.

Of interest to me, was checking the TERM environment and then using shutil.which to help determine what browsers are available. This means I can pick one of these browsers to alias to my own command for opening.

It was also interesting to see the BROWSER variable as another possible entry point. I have been using VSCode with --remote to edit on my servers and the shell is automatically populated with it.

BROWSER=/home/path/to/vscode/bin/helpers/browser.sh

Viewing the source, it’s a small shim to send things back to the host machine.

#!/usr/bin/env sh
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
ROOT="$(dirname "$(dirname "$(dirname "$(readlink -f "$0")")")")"

APP_NAME="code"
VERSION="1.107.1"
COMMIT="994fd12f8d3a5aa16f17d42c041e5809167e845a"
EXEC_NAME="code"
CLI_SCRIPT="$ROOT/out/server-cli.js"
"$ROOT/node" "$CLI_SCRIPT" "$APP_NAME" "$VERSION" "$COMMIT" "$EXEC_NAME" "--openExternal" "$@"

For my own script, I think I will shaddow www-browser but it was nice to dig into one of the core modules and easily follow the logic.