Skip to content

visionscaper/pybase

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pybase

Basic functionality for Python projects: logging setup, a Base class with built-in logger, validation utilities, and exception logging helpers.

Installation

pip install visionscaper-pybase

Logging

Setup

Call setup_logging() at application startup to configure the root logger:

from basics.logging import setup_logging, get_logger

setup_logging()

logger = get_logger('my_module')
logger.info('Application started')

Output:

20260209-151557.123 INFO: my_module::<module>: Application started

Getting a Logger

Use get_logger() to create named loggers. The default log level is DEBUG, but can be overridden:

from basics.logging import get_logger

# Module-level logger (common pattern)
module_logger = get_logger(__name__)

# Logger with custom log level
import logging
quiet_logger = get_logger('noisy_lib', log_level=logging.WARNING)

Log Format

The default format includes a timestamp prefix:

YYYYMMDD-HHMMSS.msecs LEVEL: name::funcName: message

Example:

20260209-151557.123 INFO: train::main: Starting training
20260209-151557.456 WARNING: train::main: Low memory

Environment Variables

Variable Default Description
PYBASE_LOG_PID unset Set to 1 to prefix log lines with the process ID ([PID])
PYBASE_NO_TIMESTAMP_LOG unset Set to 1 to disable the timestamp prefix
PYBASE_SUPPRESS_LEGACY_WARNINGS unset Set to 1 to suppress the deprecation warning about auto-configuration

Example with PID enabled:

[4521] 20260209-151557.123 INFO: train::main: Starting training

Custom Format

You can pass a custom format string and date format to setup_logging():

from basics.logging import setup_logging

setup_logging(
    format_string='%(asctime)s %(levelname)s %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
)

Advanced: Building Format Strings

Use build_logging_format() to programmatically construct format strings with optional timestamp and PID, independent of environment variables:

from basics.logging import build_logging_format

fmt = build_logging_format(include_timestamp=True, include_pid=True)

Legacy Compatibility

Importing basics.logging currently configures the root logger automatically via logging.basicConfig(). This ensures existing code continues to work, but will be removed in a future version. New code should call setup_logging() explicitly.

Set PYBASE_SUPPRESS_LEGACY_WARNINGS=1 to suppress the deprecation warning.

Base Class

The Base class provides a built-in logger for any class that inherits from it:

from basics.base import Base

class MyProcessor(Base):

    def __init__(self, name, **kwargs):
        super().__init__(**kwargs)
        self._log.info(f'Processor created: {name}')

    def process(self, data):
        self._log.debug(f'Processing {len(data)} items')
        return [item * 2 for item in data]

proc = MyProcessor('example')
proc.process([1, 2, 3])

Output:

20260209-151557.123 INFO: MyProcessor::__init__: Processor created: example
20260209-151557.124 DEBUG: MyProcessor::process: Processing 3 items

The logger name defaults to the class name. Override it with pybase_logger_name:

proc = MyProcessor('example', pybase_logger_name='custom_name')
# Logs will show: custom_name::__init__: ...

Customizing the Logger

Subclasses can override _pybase_get_logger_name() to customize the logger name dynamically, or _get_logger() to use a different logger implementation entirely:

class MyProcessor(Base):

    def __init__(self, name, **kwargs):
        self._name = name
        super().__init__(**kwargs)

    def _pybase_get_logger_name(self):
        return f'MyProcessor[{self._name}]'

Exception Utilities

log_exception

Log an exception with message, type, and traceback as a single atomic log record:

from basics.logging import get_logger
from basics.logging_utils import log_exception

logger = get_logger(__name__)

try:
    result = 1 / 0
except Exception as e:
    log_exception(logger, 'Division failed', e)

Exception Chain Summarization

summarize_exception_chain() produces a readable summary of chained exceptions:

from basics.logging_utils import summarize_exception_chain

try:
    try:
        raise ConnectionError('timeout')
    except ConnectionError as ce:
        raise ValueError('failed to process request') from ce
except Exception as e:
    print(summarize_exception_chain(e))

Output:

 * ValueError: failed to process request
 * ConnectionError: timeout

For a single exception (no chain), it returns a simple one-liner:

KeyError: 'missing_key'

Use include_traceback=True to append the full traceback.

get_all_exception_info

Get the class and message for each exception in the chain as a list of tuples:

from basics.logging_utils import get_all_exception_info

try:
    ...
except Exception as e:
    for cls, message in get_all_exception_info(e):
        print(f'{cls.__name__}: {message}')

Validation Utilities

Type-checking helpers available from basics.base_utils or basics.validation_utils:

from basics.base_utils import is_number, is_dict, is_sequence, is_callable

is_number(42)           # True
is_dict({'a': 1})       # True
is_sequence([1, 2, 3])  # True
is_callable(print)      # True
Function Description
is_number(n) Instance of numbers.Number
is_mapping(d) Instance of collections.abc.Mapping
is_dict(d) Instance of dict
is_bool(d) Instance of bool
is_class(c) Instance of type
is_sequence(l) Instance of collections.abc.Sequence
is_non_empty_string(s) str with length > 0
is_file(fname) os.path.isfile() check
is_callable(func) callable() check
is_int_sequence(seq) Sequence where all elements are numbers
sequences_are_compatible(a, b) Two sequences with equal length

License

MIT License. See LICENSE for details.

About

Basic functionality for python projects

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages