hbutils.testing.isolated

Overview:

Utilities for isolating environment, which can be used in testing.

This module provides tools for creating isolated testing environments by offering utilities to manage directories, entry points, input streams, and logging configurations. It helps ensure that tests run in controlled, reproducible environments without interfering with the system or other tests.

The module exports functionality from the following submodules:

  • directory: Tools for managing isolated directory structures

  • entry_point: Utilities for handling entry points in isolated environments

  • input: Input stream isolation utilities

  • logging: Logging configuration and isolation tools

isolated_directory

hbutils.testing.isolated.isolated_directory(mapping: Dict[str, str] | None = None) ContextManager[source]

Execute code in an isolated temporary directory with optional file/directory mappings.

This context manager creates a temporary directory, optionally copies specified files or directories into it based on the provided mapping, changes the working directory to the temporary location, and automatically cleans up and restores the original working directory when done.

Parameters:

mapping (Optional[Dict[str, str]]) – Dictionary mapping destination paths (relative to the isolated directory) to source paths (relative to the current directory). If None, creates an empty isolated directory.

Returns:

A context manager that yields control in the isolated directory.

Return type:

ContextManager

Raises:
  • OSError – If directory operations fail (e.g., permission issues).

  • FileNotFoundError – If a source path in the mapping does not exist.

Examples::
  • Simple usage

>>> import os
>>> import pathlib
>>> from hbutils.testing import isolated_directory
>>>
>>> with isolated_directory():
...     with open('file.txt', 'w') as f:
...         print("Line 1", file=f)
...         print("Line 2rd", file=f)
...     print(os.listdir('.'))
...     print(pathlib.Path('file.txt').read_text())
['file.txt']
Line 1
Line 2rd
>>> print(os.listdir('.'))
['hbutils', 'README.md', 'requirements.txt', ...]
  • Mapping files and directory inside

>>> import os
>>> from hbutils.testing import isolated_directory
>>>
>>> with isolated_directory({
...     'ts': 'hbutils/testing',
...     'README.md': 'README.md',
... }):
...     print(os.listdir('.'))
...     print(os.listdir('ts'))
['README.md', 'ts']
['capture', 'generator', 'isolated', '__init__.py']

isolated_stdin

hbutils.testing.isolated.isolated_stdin(v: str | List[str], mem: bool = False)[source]

Isolation for stdin stream.

This context manager allows you to mock stdin with predefined input content, useful for testing interactive programs or simulating user input.

Parameters:
  • v (Union[str, List[str]]) – Input content, either a whole string or a list of strings. If a list is provided, each element will be treated as a separate line.

  • mem (bool) – Use memory-based (StringIO) or file-based approach for stdin. Default is False which means a temporary file will be used as fake input stream. Set to True to use in-memory StringIO instead.

Returns:

A context manager for isolated stdin.

Return type:

ContextManager[None]

Examples::
>>> from hbutils.testing import isolated_stdin
>>> with isolated_stdin(['123', '456']):
...     a = int(input())
...     b = int(input())
...     print(a, b, a + b)
123 456 579
>>> with isolated_stdin('single line input', mem=True):
...     line = input()
...     print(f'Read: {line}')
Read: single line input

isolated_entry_points

hbutils.testing.isolated.isolated_entry_points(group: str, fakes: List | Dict[str, Any] | None = None, auto_import: bool = True, clear: bool = False)[source]

Isolation context manager for entry points functions.

This context manager allows for mocking or clearing entry points during testing. It supports pkg_resources.iter_entry_points, importlib.metadata.entry_points, and importlib_metadata.entry_points.

Parameters:
  • group (str) – The entry point group name to isolate.

  • fakes (Union[List, Dict[str, Any], None]) – Fake entry points to inject. Can be a list, tuple, or dict. List/tuple format: [(name, dist), object, ‘import.path’, …] Dict format: {name: dist, name: ‘import.path’, …}

  • auto_import (bool) – Auto import objects from string paths. Default is True.

  • clear (bool) – Clear original entry points if True. Default is False.

Raises:

TypeError – If fakes parameter is not of type list, tuple, dict, or None.

Example::
>>> from hbutils.testing import isolated_entry_points
>>> 
>>> # Mock plugins with a list
>>> with isolated_entry_points('my_plugin', [
...     ('quick_import_object', 'hbutils.reflection.quick_import_object'),
...     ('func_filter', filter),
...     map,
...     'hbutils.system.is_binary_file',
... ]):
...     # Entry points are mocked within this context
...     pass
>>> 
>>> # Mock plugins with a dict
>>> with isolated_entry_points('my_plugin', {
...     'func_map': map,
...     'func_binary': 'hbutils.system.is_binary_file'
... }):
...     # Entry points are mocked within this context
...     pass
>>> 
>>> # Clear all entry points for a group
>>> with isolated_entry_points('my_plugin', clear=True):
...     # No entry points available in this context
...     pass

Warning

The pkg_resources package is no longer officially supported. However, certain libraries that rely on hbutils are still in use, hence temporary support will be provided. The official guidance must be followed to migrate to importlib.metadata at the earliest opportunity. In addition, support for the pkg_resources package in this function will be discontinued in the next major version.

isolated_logger

hbutils.testing.isolated.isolated_logger(logger: str | Logger | None = None, handlers: List[Handler] | None = None, close_handlers: bool = True)[source]

Context manager for temporarily isolating and mocking loggers in the logging module.

This function allows you to temporarily replace a logger’s handlers with custom ones, and automatically restores the original handlers when exiting the context. This is particularly useful for testing or debugging scenarios where you need to capture or redirect log output.

Parameters:
  • logger (Optional[Union[str, logging.Logger]]) – Logger instance or logger name for isolation. If None, uses the root logger.

  • handlers (Optional[List[logging.Handler]]) – Initial handlers to use during isolation. If None, uses an empty list.

  • close_handlers (bool) – Whether to close handlers after exiting the context. Defaults to True.

Yield:

The isolated logger instance.

Return type:

logging.Logger

Examples::
>>> import logging
>>> from rich.logging import RichHandler  # a 3rd-party logger
>>> from hbutils.testing import isolated_logger
>>>
>>> logging.error('this is error')  # normal log
ERROR:root:this is error
>>> logging.error('this is error')
ERROR:root:this is error
>>> with isolated_logger(handlers=[  # replaced with custom handlers
...     RichHandler(),
...     logging.FileHandler('test_log.txt'),
... ]):
...     logging.error('this is error inside 1')
...     logging.error('this is error inside 2')
[11/08/22 15:39:37] ERROR    this is error inside 1                    <stdin>:5
                    ERROR    this is error inside 2                    <stdin>:6
>>> logging.error('this is error')  # change back to normal log
ERROR:root:this is error
>>> logging.error('this is error')
ERROR:root:this is error