Io

This module implements user-friendly input and output on top of the python’s standard logging library. It adds several special logging levels for printing tasks with progress bars and other useful things.

Setup

To init the logging system, call the setup() function. Ideally, this function should be called at the very beginning of the program execution, before any code had a chance to log anything. Calling setup() function multiple times completely overrides all previous settings.

By default, setup() determines logging level and whether ANSI color codes should be used based on environment variables and the state of the output stream:

  • DEBUG: print debug-level messages,

  • QUIET: only print warnings, errors, and input prompts,

  • NO_COLORS: disable colored output,

  • FORCE_COLORS: enable colored output.

You can override this logic by providing your own arguments to the setup() function.

yuio.io.setup(level: ~typing.Optional[int] = None, stream: ~typing.Any = <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>, use_colors: ~typing.Optional[bool] = None, formatter: ~logging.Formatter = <logging.Formatter object>, colors: ~typing.Optional[~typing.Dict[str, ~yuio.io.Color]] = None)[source]

Initial setup of the logging facilities.

Parameters
  • level – log output level. If not given, will check DEBUG and QUIET environment variables to determine an appropriate logging level.

  • stream – a stream where to output messages. Uses stderr by default.

  • use_colors – use ANSI escape sequences to color the output. If not given, will check NO_COLORS and FORCE_COLORS environment variables, and also if the given stream is a tty one.

  • formatter – formatter for log messages.

  • colors – mapping from tag name or logging level name to a Color. Logging level names and tag names are all lowercase.

Logging messages

Use logging functions from this module (or from the python’s logging module):

yuio.io.debug(msg: str, *args, **kwargs)[source]

Log a debug message.

yuio.io.info(msg: str, *args, **kwargs)[source]

Log an info message.

yuio.io.warning(msg: str, *args, **kwargs)[source]

Log a warning message.

yuio.io.error(msg: str, *args, **kwargs)[source]

Log an error message.

yuio.io.exception(msg: str, *args, exc_info=True, **kwargs)[source]

Log an error message and capture the current exception.

Call this function in the except clause of a try block or in an __exit__ function of a context manager to attach current exception details to the log message.

yuio.io.critical(msg: str, *args, **kwargs)[source]

Log a critical error message.

Coloring the output

By default, all log messages are colored according to their level.

If you need inline colors, you can use special tags in your log messages:

info('Using the <c:code>code</c> tag.')

You can combine multiple colors in the same tag:

info('<c:bold,green>Success!</c>')

To disable tags processing, pass no_color_tags flag to the logging’s extra object:

info(
    'this tag --> <c:color> is printed as-is',
    extra=dict(no_color_tags=True)
)

List of all tags available by default:

  • code: for inline code,

  • note: for notes, such as default values in user prompts,

  • success, failure: for indicating outcome of the program,

  • question, critical, error, warning, task, task_done, task_error, info, debug: used to color log messages,

  • bold, dim: font styles,

  • red, green, yellow, blue, magenta, cyan, normal: font colors.

You can add more tags or change colors of the existing ones by supplying the colors argument to the setup() function. This argument is a mapping from a tag name to a Color instance:

setup(
    colors=dict(
        success=FORE_BLUE | STYLE_BOLD
    )
)
class yuio.io.Color(fore: Optional[int] = None, back: Optional[int] = None, bold: bool = False, dim: bool = False)[source]

ANSI color code.

See the list of all available codes at Wikipedia.

Example:

# 31 is a color code for red
BOLD_RED = Color(fore=31, bold=True)

You can combine multiple colors:

BOLD_RED = FORE_RED | STYLE_BOLD

List of all pre-defined codes:

yuio.io.STYLE_BOLD = Color(fore=None, back=None, bold=True, dim=False)

Bold font style.

yuio.io.STYLE_DIM = Color(fore=None, back=None, bold=False, dim=True)

Dim font style.

yuio.io.FORE_NORMAL = Color(fore=39, back=None, bold=False, dim=False)

Normal foreground color.

yuio.io.FORE_BLACK = Color(fore=30, back=None, bold=False, dim=False)

Black foreground color.

yuio.io.FORE_RED = Color(fore=31, back=None, bold=False, dim=False)

Red foreground color.

yuio.io.FORE_GREEN = Color(fore=32, back=None, bold=False, dim=False)

Green foreground color.

yuio.io.FORE_YELLOW = Color(fore=33, back=None, bold=False, dim=False)

Yellow foreground color.

yuio.io.FORE_BLUE = Color(fore=34, back=None, bold=False, dim=False)

Blue foreground color.

yuio.io.FORE_MAGENTA = Color(fore=35, back=None, bold=False, dim=False)

Magenta foreground color.

yuio.io.FORE_CYAN = Color(fore=36, back=None, bold=False, dim=False)

Cyan foreground color.

yuio.io.FORE_WHITE = Color(fore=37, back=None, bold=False, dim=False)

White foreground color.

yuio.io.BACK_NORMAL = Color(fore=None, back=49, bold=False, dim=False)

Normal background color.

yuio.io.BACK_BLACK = Color(fore=None, back=40, bold=False, dim=False)

Black background color.

yuio.io.BACK_RED = Color(fore=None, back=41, bold=False, dim=False)

Red background color.

yuio.io.BACK_GREEN = Color(fore=None, back=42, bold=False, dim=False)

Green background color.

yuio.io.BACK_YELLOW = Color(fore=None, back=43, bold=False, dim=False)

Yellow background color.

yuio.io.BACK_BLUE = Color(fore=None, back=44, bold=False, dim=False)

Blue background color.

yuio.io.BACK_MAGENTA = Color(fore=None, back=45, bold=False, dim=False)

Magenta background color.

yuio.io.BACK_CYAN = Color(fore=None, back=46, bold=False, dim=False)

Cyan background color.

yuio.io.BACK_WHITE = Color(fore=None, back=47, bold=False, dim=False)

White background color.

Indicating progress

You can use the Task class to indicate status and progress of some task:

class yuio.io.Task(msg: str, *args)[source]

Context manager that automatically reports tasks and their progress.

This context manager accepts a name of the task being done, along with all the usual logging parameters.

When context is entered, it emits a message that the task is in progress.

When context is exited, it emits a message that the task has finished.

If an exception is thrown out of context, it emits a message that the task has failed.

Example:

with Task('Fetching data') as task:
    for i, url in enumerate(urls):
        url.fetch()
        task.progress((i, len(urls)))

This will output the following:

Fetching data... [=====>              ] 30% (3 / 10)

Note that you don’t have to use it in a context:

task = Task('Fetching data')
task.begin()

for i, url in enumerate(urls):
    url.fetch()
    task.progress(float(i) / len(urls))

task.done()

In this case, however, it’s up to you to catch and report errors.

begin()[source]

Emit a message about task being in progress.

progress(progress: Union[int, float, Tuple[int, int], Tuple[int, int, int]])[source]

Indicate progress of ths task.

Parameters

progress

Progress of the task. Could be one of three things:

  • a floating point number between 0 and 1;

  • a tuple of two ints, first is the number of completed jobs, and second is the total number of jobs;

  • a tuple of three ints, first is the number of completed jobs, second is the number of in-progress jobs, and the third is the total number of jobs.

done()[source]

Emit a message that the task is finished successfully.

error()[source]

Emit a message that the task is finished with an error.

exception()[source]

Emit a message that the task is finished, capture current exception.

Querying user input

To query some input from a user, there’s the ask() function:

yuio.io.ask(msg: str, *args, default: Optional[str] = 'None', input_description: Optional[str] = 'None', default_description: Optional[str] = 'None', hidden: bool = 'False') str[source]
yuio.io.ask(msg: str, *args, parser: Parser[T], default: Optional[T] = 'None', input_description: Optional[str] = 'None', default_description: Optional[str] = 'None', hidden: bool = 'False') T

Ask user to provide an input, then parse it and return a value.

If launched in a non-interactive environment, returns the default if one is present, or raises a UserIoError.

Example:

answer = ask(
    'Do you want a choco bar?',
    parser=yuio.parse.Bool(),
    default=True,
)
Parameters
  • msg – prompt that will be sent to the user.

  • args – arguments for prompt formatting.

  • parser – how to parse and verify the input. See yuio.parse for more info.

  • default – if given, this value will be returned if no input is provided.

  • input_description – a string that describes expected input, like 'yes/no' for boolean inputs. By default, inferred from the given parser.

  • default_description – a string that describes the default value. By default, inferred from the given parser and repr of the default value.

  • hidden – if enabled, treats input as password, and uses secure input methods. This option also hides errors from the parser, because they may contain user input.

You can prompt user to edit something with the edit() function. Note that this function doesn’t add any header with an explanation to the given text, so you will need to do it yourself:

yuio.io.edit(text: str, comment_marker: Optional[str] = '#', editor: Optional[str] = None) str[source]

Ask user to edit some text.

This function creates a temporary file with the given text and opens it in an editor. After editing is done, it strips away all lines that start with comment_marker, if one is given.

If editor is not available or returns a non-zero exit code, a UserIoError is raised.

If launched in a non-interactive environment, returns the text unedited (comments are still removed, though).

There are some helper functions and classes:

yuio.io.is_interactive() bool[source]

Check if we’re running in an interactive environment.

yuio.io.detect_editor() Optional[str][source]

Detect an editor executable.

This function checks the EDITOR environment variable. If it’s not found, it checks whether nano or vi are available. Otherwise, it returns None.

class yuio.io.UserIoError[source]

Raised when interaction with user fails.