Skip to content

Commands

High-level command objects that back the CLI verbs. They can also be used directly as library entry points when you want terminal-formatted output without calling the CLI.

Fetcher

Bases: HTTPClient

Fetch a URL and print the parsed response to the terminal.

Inherits from :class:~go2web.http.client.HTTPClient so all constructor arguments (cache, use_cache) are available.

The response body is parsed by the appropriate :class:~go2web.http.parsers.abstract_parser.Parser for its content type, then displayed inside a styled Rich panel. Errors are caught and printed as red error panels rather than propagating as exceptions.

Example

from go2web.commands.fetch import Fetcher fetcher = Fetcher(use_cache=False) text = fetcher.fetch("https://example.com")

Source code in src/go2web/commands/fetch.py
class Fetcher(HTTPClient):
    """Fetch a URL and print the parsed response to the terminal.

    Inherits from :class:`~go2web.http.client.HTTPClient` so all constructor
    arguments (``cache``, ``use_cache``) are available.

    The response body is parsed by the appropriate
    :class:`~go2web.http.parsers.abstract_parser.Parser` for its content type,
    then displayed inside a styled Rich panel. Errors are caught and printed
    as red error panels rather than propagating as exceptions.

    Example:
        >>> from go2web.commands.fetch import Fetcher
        >>> fetcher = Fetcher(use_cache=False)
        >>> text = fetcher.fetch("https://example.com")
    """

    def fetch(self, url: str) -> str:
        """Fetch *url*, parse the body, and print it to stdout.

        Args:
            url: The URL to fetch. A missing ``https://`` scheme is added
                automatically by the underlying :class:`~go2web.http.client.HTTPClient`.

        Returns:
            The parsed body string on success, or the error message string
            when an :class:`~go2web.http.client.HTTPError` or
            :class:`~go2web.http.parsers.exceptions.ParseError` is raised.
        """
        try:
            response = self.get(url)
            parser = ParserManager().get_parser(response.get_content_type())
            text = parser.parse(response.body)
            print_result(text, title=url)
            return text
        except (HTTPError, ParseError) as e:
            print_error(str(e))
            return str(e)

fetch(url)

Fetch url, parse the body, and print it to stdout.

Parameters:

Name Type Description Default
url str

The URL to fetch. A missing https:// scheme is added automatically by the underlying :class:~go2web.http.client.HTTPClient.

required

Returns:

Type Description
str

The parsed body string on success, or the error message string

str

when an :class:~go2web.http.client.HTTPError or

str

class:~go2web.http.parsers.exceptions.ParseError is raised.

Source code in src/go2web/commands/fetch.py
def fetch(self, url: str) -> str:
    """Fetch *url*, parse the body, and print it to stdout.

    Args:
        url: The URL to fetch. A missing ``https://`` scheme is added
            automatically by the underlying :class:`~go2web.http.client.HTTPClient`.

    Returns:
        The parsed body string on success, or the error message string
        when an :class:`~go2web.http.client.HTTPError` or
        :class:`~go2web.http.parsers.exceptions.ParseError` is raised.
    """
    try:
        response = self.get(url)
        parser = ParserManager().get_parser(response.get_content_type())
        text = parser.parse(response.body)
        print_result(text, title=url)
        return text
    except (HTTPError, ParseError) as e:
        print_error(str(e))
        return str(e)

Searcher

Search the web and interactively open a result.

Combines a :class:~go2web.search.engines.base.BaseSearchEngine with a :class:~go2web.commands.fetch.Fetcher to implement the full go2web search workflow:

  1. Query the search engine.
  2. Present results in a questionary interactive picker.
  3. Fetch and display the selected result.

The engine and fetcher are injected so they can be swapped in tests or extended for custom workflows.

Example

Using the default Bing engine:

from go2web.commands.search import Searcher Searcher().search("python packaging")

Using a custom engine and disabling the fetcher cache:

from go2web.commands.fetch import Fetcher from go2web.search.engines.bing import BingEngine Searcher(engine=BingEngine(), fetcher=Fetcher(use_cache=False)).search("python")

Source code in src/go2web/commands/search.py
class Searcher:
    """Search the web and interactively open a result.

    Combines a :class:`~go2web.search.engines.base.BaseSearchEngine` with a
    :class:`~go2web.commands.fetch.Fetcher` to implement the full
    ``go2web search`` workflow:

    1. Query the search engine.
    2. Present results in a ``questionary`` interactive picker.
    3. Fetch and display the selected result.

    The engine and fetcher are injected so they can be swapped in tests or
    extended for custom workflows.

    Example:
        Using the default Bing engine:

        >>> from go2web.commands.search import Searcher
        >>> Searcher().search("python packaging")

        Using a custom engine and disabling the fetcher cache:

        >>> from go2web.commands.fetch import Fetcher
        >>> from go2web.search.engines.bing import BingEngine
        >>> Searcher(engine=BingEngine(), fetcher=Fetcher(use_cache=False)).search("python")
    """

    def __init__(
        self,
        engine: BaseSearchEngine | None = None,
        fetcher: Fetcher | None = None,
        limit: int = 10,
    ) -> None:
        """Initialise the searcher.

        Args:
            engine: The search engine backend to use. Defaults to
                :class:`~go2web.search.engines.bing.BingEngine`.
            fetcher: The fetcher used to open the selected result. Defaults to
                a :class:`~go2web.commands.fetch.Fetcher` with caching enabled.
            limit: Number of results to request from the engine (default ``10``).
        """
        self._engine = engine or BingEngine()
        self._fetcher = fetcher or Fetcher()
        self._limit = limit

    def search(self, query: str, limit: int | None = None) -> None:
        """Run a search for *query* and open the chosen result.

        Prints an interactive picker to the terminal. If the user selects a
        result, it is fetched and displayed via :meth:`~go2web.commands.fetch.Fetcher.fetch`.
        Choosing **Cancel** exits without fetching.

        Args:
            query: The search query string.
            limit: Override the instance-level result limit for this call.
        """
        try:
            results = self._engine.search(query, limit=limit or self._limit)
        except Exception as exc:
            print_error(f"Search failed: {exc}")
            raise SystemExit(1) from exc

        if not results:
            print_info("No results found.")
            return

        selected = self._prompt(results)

        if not selected:
            return

        print_info(f"Fetching: {selected.url}")
        self._fetcher.fetch(selected.url)

    def _prompt(self, results: list[SearchResult]) -> SearchResult | None:
        """Display an interactive picker and return the chosen result.

        Args:
            results: The list of search results to display.

        Returns:
            The selected :class:`~go2web.search.result.SearchResult`, or
            ``None`` when the user chooses **Cancel** or exits.
        """
        choices = [Choice(title=f"{r.rank:>2}. {r.title}  {r.url}", value=r) for r in results]
        choices.append(Choice(title="Cancel", value=""))

        return questionary.select(
            "Select a result to open:",
            choices=choices,
        ).ask()

__init__(engine=None, fetcher=None, limit=10)

Initialise the searcher.

Parameters:

Name Type Description Default
engine BaseSearchEngine | None

The search engine backend to use. Defaults to :class:~go2web.search.engines.bing.BingEngine.

None
fetcher Fetcher | None

The fetcher used to open the selected result. Defaults to a :class:~go2web.commands.fetch.Fetcher with caching enabled.

None
limit int

Number of results to request from the engine (default 10).

10
Source code in src/go2web/commands/search.py
def __init__(
    self,
    engine: BaseSearchEngine | None = None,
    fetcher: Fetcher | None = None,
    limit: int = 10,
) -> None:
    """Initialise the searcher.

    Args:
        engine: The search engine backend to use. Defaults to
            :class:`~go2web.search.engines.bing.BingEngine`.
        fetcher: The fetcher used to open the selected result. Defaults to
            a :class:`~go2web.commands.fetch.Fetcher` with caching enabled.
        limit: Number of results to request from the engine (default ``10``).
    """
    self._engine = engine or BingEngine()
    self._fetcher = fetcher or Fetcher()
    self._limit = limit

search(query, limit=None)

Run a search for query and open the chosen result.

Prints an interactive picker to the terminal. If the user selects a result, it is fetched and displayed via :meth:~go2web.commands.fetch.Fetcher.fetch. Choosing Cancel exits without fetching.

Parameters:

Name Type Description Default
query str

The search query string.

required
limit int | None

Override the instance-level result limit for this call.

None
Source code in src/go2web/commands/search.py
def search(self, query: str, limit: int | None = None) -> None:
    """Run a search for *query* and open the chosen result.

    Prints an interactive picker to the terminal. If the user selects a
    result, it is fetched and displayed via :meth:`~go2web.commands.fetch.Fetcher.fetch`.
    Choosing **Cancel** exits without fetching.

    Args:
        query: The search query string.
        limit: Override the instance-level result limit for this call.
    """
    try:
        results = self._engine.search(query, limit=limit or self._limit)
    except Exception as exc:
        print_error(f"Search failed: {exc}")
        raise SystemExit(1) from exc

    if not results:
        print_info("No results found.")
        return

    selected = self._prompt(results)

    if not selected:
        return

    print_info(f"Fetching: {selected.url}")
    self._fetcher.fetch(selected.url)