pallets / pallets/click

[Feature] for embedding Click with custom stdout/stderr instances

Open
#2,361 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

parsing
Dominant language
Python
Stars
17.7k
Forks
2.3k
Avg merge
1d 30m
Merged PRs (30d)
18

Description

Along with #2360, I also encountered another issue that there is no public API to override the stdout/stderr instances used by Click contextually and globally. The only way found is that manually replacing click.utils._default_text_stdout and click.utils._default_text_stderr private functions internally used by click.echo() and other console output handlers in Click.

Could we have a public API to achieve this?

import sys
from contextvars import ContextVar, copy_context
from typing import TextIO

import click
from prompt_toolkit.contrib.telnet.server import TelnetConnection

current_stdout: ContextVar[TextIO] = ContextVar("current_stdout")


def _get_current_stdout() -> TextIO:
    stdout = current_stdout.get(None)
    if stdout is None:
        return sys.stdout
    else:
        return stdout


def _get_current_stderr() -> TextIO:
    stdout = current_stdout.get(None)
    if stdout is None:
        return sys.stderr
    else:
        return stdout


@monitor_cli.command()
def help(ctx: click.Context) -> None:
    """Show the list of commands"""
    # You may think that we can just add `file` argument in this simple case,
    # but still there are other non-accessible internal usage inside Click
    # such as the default help option callback, etc.
    click.echo(monitor_cli.get_help(ctx))


...


async def start_server(...):
    click.utils._default_text_stdout = _get_current_stdout
    click.utils._default_text_stderr = _get_current_stderr
    ...


async def handle_telnet_connection(connection: TelnetConnection):
    ...
    # There may be multiple concurrent connections!
    current_stdout_token = current_stdout.set(connection.stdout)
    try:
        ctx = copy_context()
        ctx.run(monitor_cli.main, ...)
    finally:
        current_stdout.reset(current_stdout_token)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/click/utils.py, especially _default_text_stdout and _default_text_stderr, and trace how click.echo() and the default help option callback obtain output streams. Review the proposed contextual and global use cases, including concurrent connections through monitor_cli.main. Done means a documented public API replaces private-function overrides while supporting both scopes.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.