Techniques

tech_niques which are logging related as well as code inspection

Conveniently exports all technique helpers

Module private variables

logging_strict.tech_niques.__all__: tuple[str, str, str, str, str, str, str, str, str] = ("FuncWrapper", "get_locals", "get_locals_dynamic",    "is_class_attrib_kind", "ClassAttribTypes",    "LoggerRedirector", "captureLogs", "detect_coverage", "CaptureOutput")

This modules exports

Module objects

class logging_strict.tech_niques.ClassAttribTypes(enum.Enum)
As understood by :py:obj:`inspect.classify_class_attrs`
CLASSMETHOD: str = 'class method'

Is this a class classmethod?

STATICMETHOD: str = 'static method'

Is this a class staticmethod

PROPERTY: str = 'property'

Is this a class property?

METHOD: str = 'method'

Is this a class normal method

DATA: str = 'data'

Is this class data

class logging_strict.tech_niques.CaptureOutput

Context manager to capture both sys.stdout and sys.stderr streams

__slots__: tuple[str, str, str, str] = ("_stdout_output", "_stderr_output", "_stdout", "_stderr")

Reduce class memory footprint

_stderr
_stderr_output
_stdout
_stdout_output
property stderr

Getter of captured stderr

Returns:

Captured stderr

Return type:

str

property stdout

Getter of captured stdout

Returns:

Captured stdout

Return type:

str

class logging_strict.tech_niques.FuncWrapper(func)

Wraps a function to provide basic info about it.

Variables:

func ((types.FunctionType | types.MethodType | types.BuiltinFunctionType | types.BuiltinMethodType | types.WrapperDescriptorType | types.MethodWrapperType | types.MethodDescriptorType | types.ClassMethodDescriptorType)) – the function to wrap

static _get_method_parent(meth)

Returns the class of the parent of the specified method.

Parameters:

meth – the method to check

Returns:

the class of the parent of the specified method

Return type:

type | None

property cls

Returns the function’s parent class.

Returns:

the parent class

Return type:

type | None

property cls_name

Returns the function’s parent class name.

Returns:

the parent class name

Return type:

str | None

property full_name

Returns the function’s full name with the class included.

Returns:

the full name

Return type:

str

get_dotted_path()

Returns the function’s full path with class and package name.

Returns:

the full path

Return type:

str

Raises:

property module

Returns the function’s parent module.

Returns:

the parent module

Return type:

types.ModuleType | None

property module_filename

Returns the function’s module filename.

Returns:

the module filename

Return type:

str | None

property module_name

Returns the function’s parent module name.

Returns:

the parent module name

Return type:

str | None

property name

Returns the function’s name.

Returns:

the function’s name

Return type:

str

property package_name

Returns the function’s package name.

Returns:

the package name

Return type:

str

property root_package_name

Returns the function’s root package name.

Returns:

the root package name

Return type:

str

class logging_strict.tech_niques.LoggerRedirector

unittest redirects sys.stdout and sys.stderr. Keep a reference to the real streams so we can later be reverted. Logging goes to the wrong IO streams. Upon failure, there are no log messages.

Redirect to the correct IO streams.

Required for the unittest discover command: --buffer option

_real_stdout

Hold sys.stdout reference. Restores sys.stdout at the end of the context manager

_real_stderr

Hold sys.stderr reference. Restores sys.stdout at the end of the context manager

Usage

In a unittest module (level), setup logging.basicConfig

>>> import sys
>>> import logging
>>> logging.basicConfig(
...     format='%(module)s %(levelname)s: %(message)s',
...     level=logging.INFO,
...     stream=sys.stdout,
... )

In a unittest module class

import sys
import logging
from logging_strict.tech_niques import LoggerRedirector
def setUp(self):
    # unittest has reassigned sys.stdout and sys.stderr by this point

    g_module = f"[app_name].tests.test_[module name]"
    self._LOGGER = logging.getLogger(g_module)

    # %(asctime)s
    logging.basicConfig(
         format="%(levelname)s %(module)s: %(message)s",
         level=logging.INFO,
         stream=sys.stdout,
    )
    LoggerRedirector.redirect_loggers(
        fake_stdout=sys.stdout,
        fake_stderr=sys.stderr,
    )
def tearDown(self):
    # unittest will revert sys.stdout and sys.stderr after this
    LoggerRedirector.reset_loggers(
        fake_stdout=sys.stdout,
        fake_stderr=sys.stderr,
    )

See also

LoggerRedirector source

LoggerRedirector author

In unittests, showing log messages on failure

_real_stderr = <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>
_real_stdout = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
static all_loggers()

Get loggers

Returns:

space separated tests (method name(s))

Return type:

Sequence[logging.Logger]

classmethod redirect_loggers(fake_stdout=None, fake_stderr=None) None

unittest temporarily switch the IO streams. Use the unittest temporary IO streams

Call in unittest class setUp method

Parameters:
  • fake_stdout (TextIO) – unittest temporary stdout IO stream

  • fake_stderr (TextIO) – unittest temporary stderr IO stream

classmethod reset_loggers(fake_stdout=None, fake_stderr=None) None

unittest temporarily switch the IO streams. Switch back to the normal IO streams

Call in unittest class tearDown method

Parameters:
  • fake_stdout (TextIO) – unittest temporary stdout IO stream

  • fake_stderr (TextIO) – unittest temporary stderr IO stream

logging_strict.tech_niques.captureLogs(logger=None, level=None, format_='%(levelname)s %(module)s %(funcName)s: %(lineno)d: %(message)s')

A context manager to capture logging a loggers logging output

Example:

import logging
from logging_strict.tech_niques import captureLogs

with captureLogs('foo', level='INFO') as cm:
    logging.getLogger('foo').info('first message')
    logging.getLogger('foo.bar').error('second message')
print(cm.output)

The watcher ( logging_strict.tech_niques.logging_capture._LoggingWatcher ) has attributes:

  • output

  • records

    unformatted records

Parameters:
  • logger (str | logging.Logger | None) – Default None. logger or logger name

  • level (str | int | None) – Default None. Logging level

  • format_ (str | None) – Default None. Can override logging format spec

Returns:

Context manager yields one logging_strict.tech_niques.logging_capture._LoggingWatcher. Which stores the log records/messages

Return type:

Iterator[logging_strict.tech_niques.logging_capture._LoggingWatcher]

See also

Context manager howto, PEP 343

logging_strict.tech_niques.detect_coverage() bool

Running by coverage and running by unittest behavior differs!

Returns:

True if runner is coverage otherwise False

Return type:

bool

See also

Detecting runner

Todo

why coverage overrides logging.config?

When run by coverage, logging level becomes logging.INFO When run by unittest, logging level is same as logging.config

How to get the same behavior

logging_strict.tech_niques.get_locals(func_path, func, /, *args, **kwargs)

Uses patch to retrieve the tested functions locals and return value!

See this module docs for example

Limitation: the function must end with a single return, not yield or raise.

Parameters:
Returns:

Tuple containing return value and the locals

Return type:

tuple[logging_strict.tech_niques.context_locals._T, dict[str, Any]]

Deprecated since version 1.6.0: get_locals_dynamic does not need func_path and has support for class methods.

Removal not planned. Very popular, widely used, transition low priority and will take time.

logging_strict.tech_niques.get_locals_dynamic(func, /, *args, **kwargs)

Uses patch to retrieve the tested functions locals and return value!

See this module docs for example

Limitation: the function must end with a single return, not yield or raise.

Parameters:
Returns:

Tuple containing return value and the locals

Return type:

tuple[logging_strict.tech_niques.context_locals._T, dict[str, Any]]

logging_strict.tech_niques.is_class_attrib_kind(cls, str_m, kind)

For testing an ABC implementation

Parameters:
Returns:

True if is expected logging_strict.tech_niques.is_class_attrib_kind.params.kind otherwise False

Return type:

bool

Raises: