API Reference

Data Models

sawmill.models.message

Message and FileRef data models for sawmill.

These models represent the core data structures for log messages. Plugins create Message instances; the base app is just an orchestrator.

class sawmill.models.message.FileRef(**data)

Bases: BaseModel

Reference to a source file location extracted from a log message.

Parameters:
  • path (str)

  • line (int | None)

model_config: ClassVar[ConfigDict] = {'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

path: str
line: int | None
class sawmill.models.message.Message(**data)

Bases: BaseModel

A logical log message (single or multi-line).

Plugins handle grouping of multi-line messages and create Message instances. The base app operates on these as complete logical units.

Parameters:
start_line

First line number in the source log file (1-indexed).

end_line

Last line number in the source log file (1-indexed).

raw_text

Complete original text including all lines.

content

Extracted/cleaned message content.

severity

Severity level (e.g., “error”, “warning”, “info”, “critical_warning”).

message_id

Tool-specific message ID (e.g., “Vivado 12-3523”).

category

Optional category for grouping (e.g., “timing”, “drc”).

file_ref

Optional reference to source file mentioned in message.

metadata

Plugin-specific metadata for custom grouping/filtering. Plugins can populate this with tool-specific fields like “hierarchy”, “phase”, “clock_domain”, etc.

model_config: ClassVar[ConfigDict] = {'frozen': False}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

start_line: int
end_line: int
raw_text: str
content: str
severity: str | None
message_id: str | None
category: str | None
file_ref: FileRef | None
metadata: dict[str, str]
matches_filter(pattern, case_sensitive=True)

Check if this message matches the given regex pattern.

Matches are performed against the raw_text (full original message).

Parameters:
  • pattern (str) – Regular expression pattern to match.

  • case_sensitive (bool) – Whether to perform case-sensitive matching.

Return type:

bool

Returns:

True if the pattern matches, False otherwise.

get_field_value(field_id)

Get the value of a field by its ID.

This method supports both builtin fields and metadata fields, allowing plugins to define custom grouping dimensions.

Parameters:

field_id (str) – The field identifier. Can be: - “severity”, “message_id”, “category” (builtin) - “file” (uses file_ref.path) - Any key in the metadata dict

Return type:

str | None

Returns:

The field value as a string, or None if not set.

sawmill.models.plugin_api

Data models for plugin API definitions.

This module provides structured models for plugin-defined metadata such as severity levels and grouping fields.

class sawmill.models.plugin_api.SeverityLevel(**data)

Bases: BaseModel

Definition of a severity level.

Plugins use this to declare their severity levels in order from most to least severe. This allows the base app to properly order and style messages regardless of the tool’s naming conventions.

Level numbering contract:

Levels MUST be consecutive integers starting at 0. Level 0 is the lowest severity (informational/note). Higher numbers indicate more severe levels.

Parameters:
id

Internal identifier (lowercase, e.g., “error”, “warning”).

name

Human-readable display name (e.g., “Error”, “Warning”).

level

Numeric level for comparison (0 = lowest, higher = more severe).

style

Rich style string for terminal display.

model_config: ClassVar[ConfigDict] = {'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

id: str
name: str
level: int
style: str
class sawmill.models.plugin_api.GroupingField(**data)

Bases: BaseModel

Definition of a field available for grouping messages.

Plugins use this to declare what dimensions are available for grouping and sorting messages.

Parameters:
  • id (str)

  • name (str)

  • field_type (Literal['builtin', 'metadata', 'file_ref'])

  • description (str)

  • sort_order (list[str] | None)

id

Field identifier (used in CLI as –group-by value).

name

Human-readable display name.

field_type

How to extract the value from a Message: - “builtin”: Use Message.severity, Message.message_id, etc. - “metadata”: Use Message.metadata[id] - “file_ref”: Use Message.file_ref.path

description

Optional description for help text.

sort_order

Optional list of values for custom sort ordering.

model_config: ClassVar[ConfigDict] = {'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

id: str
name: str
field_type: Literal['builtin', 'metadata', 'file_ref']
description: str
sort_order: list[str] | None
sawmill.models.plugin_api.severity_levels_from_dicts(dicts)

Convert a list of dictionaries to SeverityLevel objects.

Parameters:

dicts (list[dict]) – List of dictionaries with severity level data.

Return type:

list[SeverityLevel]

Returns:

List of SeverityLevel objects.

sawmill.models.plugin_api.grouping_fields_from_dicts(dicts)

Convert a list of dictionaries to GroupingField objects.

Parameters:

dicts (list[dict]) – List of dictionaries with grouping field data.

Return type:

list[GroupingField]

Returns:

List of GroupingField objects.

sawmill.models.waiver

Waiver data models for sawmill.

Waivers are for CI acceptance (pass/fail decisions with audit trail). They are distinct from suppressions which are for display filtering.

class sawmill.models.waiver.Waiver(**data)

Bases: BaseModel

A waiver entry for CI acceptance.

Waivers indicate that a specific error or warning has been reviewed and accepted, so it should not cause CI failure.

Every waiver matches on message_id (required). An optional content_pattern narrows the match to specific instances of that message ID.

Parameters:
  • message_id (str)

  • content_match (Literal['raw', 'regex'] | None)

  • content_pattern (str | None)

  • reason (str)

  • author (str)

  • date (str)

  • expires (str | None)

  • ticket (str | None)

message_id

The message ID to match (exact match, required).

content_match

How to interpret content_pattern (“raw” or “regex”). None when content_pattern is absent.

content_pattern

Optional pattern to match against message content. If absent/empty, waiver matches all instances of the message_id.

reason

Explanation of why this is waived.

author

Who created this waiver.

date

When this waiver was created (ISO format string).

expires

Optional expiration date (ISO format string).

ticket

Optional reference to issue tracker.

model_config: ClassVar[ConfigDict] = {'frozen': False}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

message_id: str
content_match: Literal['raw', 'regex'] | None
content_pattern: str | None
reason: str
author: str
date: str
expires: str | None
ticket: str | None
class sawmill.models.waiver.WaiverFile(**data)

Bases: BaseModel

A collection of waivers loaded from a file.

Parameters:
tool

The tool this waiver file is for (e.g., “vivado”).

waivers

List of waiver entries.

path

Optional path to the source file (for error reporting).

model_config: ClassVar[ConfigDict] = {'frozen': False}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

tool: str | None
waivers: list[Waiver]
path: str | None

sawmill.models.filter_def

FilterDefinition data model for sawmill.

This model represents filter patterns that can be applied to log messages.

class sawmill.models.filter_def.FilterDefinition(**data)

Bases: BaseModel

A filter pattern with metadata.

Plugins can provide filter definitions, and users can also create them. Filters are applied by the FilterEngine to select matching messages.

Parameters:
  • id (str)

  • name (str)

  • pattern (str)

  • source (str | None)

  • description (str | None)

id

Unique identifier for the filter.

name

Human-readable name for display.

pattern

Regular expression pattern to match.

source

Origin of the filter (e.g., “plugin:vivado”, “config”, “user”).

description

Optional description of what this filter matches.

id: str
name: str
pattern: str
source: str | None
description: str | None
classmethod validate_regex(v)

Validate that the pattern is a valid regular expression.

Return type:

str

Parameters:

v (str)

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

Plugin Interface

sawmill.plugin.hookspec

Hook specifications for sawmill plugins.

This module defines the pluggy hook specification that plugins must implement. Plugins use the @hookimpl decorator to register their implementations.

class sawmill.plugin.hookspec.SawmillHookSpec

Bases: object

Hook specification defining the plugin interface.

Plugins implement these hooks to provide log parsing capabilities. The base application calls these hooks through the pluggy PluginManager.

can_handle(path)

Determine if this plugin can handle the given log file.

Plugins should examine the file (name, initial content, etc.) and return a confidence score indicating how well they can parse it.

Parameters:

path (Path) – Path to the log file to check.

Returns:

  • 0.0: Cannot handle this file

  • 0.5: Might be able to handle (ambiguous)

  • 1.0: Definitely can handle this file

The plugin with the highest confidence score will be selected. If no plugin has confidence > 0.5, an error is raised.

Return type:

float

load_and_parse(path)

Load and parse the log file into messages.

This is the primary parsing hook. Plugins should: 1. Open and read the file (handling encoding issues) 2. Parse each line/section into logical messages 3. Group multi-line messages into single Message objects 4. Extract severity, message IDs, and other metadata

Parameters:

path (Path) – Path to the log file to parse.

Return type:

list[Message]

Returns:

List of Message objects, each representing a complete logical message (single or multi-line). Messages should have start_line and end_line set correctly for multi-line messages.

get_filters()

Get filter definitions provided by this plugin.

Plugins can provide pre-defined filters for common use cases, such as filtering by severity level or message category.

Return type:

list[FilterDefinition]

Returns:

List of FilterDefinition objects that users can enable/disable.

extract_file_reference(content)

Extract a file reference from message content.

Many log messages reference source files with line numbers. This hook extracts that information for navigation.

Parameters:

content (str) – The message content to search for file references.

Return type:

FileRef | None

Returns:

FileRef if a reference is found, None otherwise.

get_severity_levels()

REQUIRED: Get severity levels supported by this plugin.

Plugins MUST implement this hook to define their severity levels. The base app uses these levels for: - Filtering messages by severity (–severity, –fail-on) - Sorting and grouping messages - Styling output (colors in terminal) - Determining pass/fail in –check mode

Level numbering contract:

Plugins MUST use consecutive integers starting at 0. Level 0 is the lowest severity (informational/note). Higher numbers indicate more severe levels.

Returns:

[
{

“id”: “fatal”, # Internal identifier (lowercase) “name”: “Fatal”, # Display name “style”: “red bold”, # Rich style for display “level”: 3, # Numeric level (0 = lowest, higher = more severe)

}, {

”id”: “error”, “name”: “Error”, “style”: “red”, “level”: 2,

}, {

”id”: “warning”, “name”: “Warning”, “style”: “yellow”, “level”: 1,

}, {

”id”: “note”, “name”: “Note”, “style”: “cyan”, “level”: 0, # Level 0 = lowest (informational)

},

]

Return type:

list[dict]

The numeric “level” field is used for threshold comparisons: - –check defaults to failing on the second-lowest level and above - –fail-on <severity> sets the threshold to that severity’s level

Raises:

NotImplementedError – If not implemented by the plugin.

Return type:

list[dict]

get_grouping_fields()

Get fields available for grouping and sorting.

Plugins can declare what dimensions are available for grouping messages. This includes both standard fields (severity, id, file) and custom metadata fields populated by the plugin.

Returns:

[
{

“id”: “severity”, # Field identifier “name”: “Severity”, # Display name “type”: “builtin”, # “builtin”, “metadata”, or “file_ref” “description”: “Group by message severity level”,

}, {

”id”: “hierarchy”, “name”: “Design Hierarchy”, “type”: “metadata”, # Uses Message.metadata[“hierarchy”] “description”: “Group by RTL hierarchy path”,

]

Return type:

list[dict]

Standard field types: - “builtin”: Uses Message attributes (severity, message_id, category) - “metadata”: Uses Message.metadata[field_id] - “file_ref”: Uses Message.file_ref.path

If not implemented, the base app provides default groupings: severity, id, file, category.

Core Modules

sawmill.core.filter

FilterEngine for applying regex filters to log messages.

This module provides the core filtering logic that operates on plugin output. The FilterEngine applies filters, suppressions, and provides statistics.

class sawmill.core.filter.FilterEngine

Bases: object

Engine for applying regex filters to log messages.

The FilterEngine operates on lists of Message objects provided by plugins. It supports single filter matching, multi-filter combinations (AND/OR modes), and suppression patterns for hiding unwanted messages.

apply_filter(pattern, messages, case_sensitive=True)

Apply a single regex filter to messages.

Parameters:
  • pattern (str) – Regular expression pattern to match against message raw_text.

  • messages (list[Message]) – List of messages to filter.

  • case_sensitive (bool) – Whether to perform case-sensitive matching.

Return type:

list[Message]

Returns:

List of messages that match the pattern. Returns empty list if pattern is invalid regex.

apply_filters(filters, messages, mode='AND', active_ids=None)

Apply multiple filters to messages with AND or OR logic.

Parameters:
  • filters (list[FilterDefinition]) – List of filter definitions to apply.

  • messages (list[Message]) – List of messages to filter.

  • mode (Literal['AND', 'OR']) – “AND” requires all active filters to match, “OR” requires any active filter to match.

  • active_ids (set[str] | None) – Set of filter IDs to apply. If None, all filters are applied.

Return type:

list[Message]

Returns:

List of messages that match according to the mode. If no active filters, returns all messages.

apply_suppressions(patterns, messages)

Apply suppression patterns to remove matching messages.

Suppressions are patterns that indicate messages to hide (exclude). This is for display filtering, not CI acceptance (use waivers for that).

Parameters:
  • patterns (list[str]) – List of regex patterns for messages to suppress.

  • messages (list[Message]) – List of messages to filter.

Return type:

list[Message]

Returns:

List of messages that do NOT match any suppression pattern.

apply_suppress_ids(suppress_ids, messages)

Partition messages into kept and suppressed by message ID.

Messages whose message_id is in suppress_ids are suppressed. Messages with no message_id are always kept.

Parameters:
  • suppress_ids (set[str]) – Set of message IDs to suppress.

  • messages (list[Message]) – List of messages to partition.

Return type:

tuple[list[Message], list[Message]]

Returns:

Tuple of (kept, suppressed) message lists.

sawmill.core.filter.filter_by_severity_toggles(messages, toggles)

Filter messages by severity toggle state.

Messages with None severity are always included. Severity IDs not present in toggles default to True (visible).

Parameters:
  • messages (list[Message]) – List of messages to filter.

  • toggles (dict[str, bool]) – Dict mapping severity ID to enabled state.

Return type:

list[Message]

Returns:

List of messages whose severity is enabled.

sawmill.core.filter.match_message_id(message_id, pattern)

Check if a message ID matches a pattern (supports wildcards).

Uses fnmatch for glob-style pattern matching: - ‘*’ matches any sequence of characters - ‘?’ matches any single character

Parameters:
  • message_id (str | None) – The message ID to check (may be None).

  • pattern (str) – The pattern to match against (e.g., “Synth 8-*”).

Return type:

bool

Returns:

True if the message ID matches the pattern.

sawmill.core.aggregation

Aggregation and grouping utilities for messages.

This module provides classes for aggregating messages by severity, ID, file, or category - useful for summary views and grouped output.

The Aggregator class can be configured with plugin-provided severity levels and grouping fields for tool-specific customization.

sawmill.core.aggregation.make_severity_sort_key(severity_levels)

Create a severity sort key function.

Parameters:

severity_levels (list[SeverityLevel]) – List of severity levels from plugin. Required.

Returns:

A function that returns a sort key for a severity string.

Raises:

ValueError – If severity_levels is empty.

class sawmill.core.aggregation.MessageStats(key, severity=None, count=0, messages=<factory>, files_affected=<factory>)

Bases: object

Statistics for messages grouped by a key.

Parameters:
  • key (str)

  • severity (str | None)

  • count (int)

  • messages (list)

  • files_affected (set)

key

The grouping key (ID, file, category, or severity).

severity

The severity level for this group (if grouping by ID/file/category).

count

Number of messages in this group.

messages

List of messages in this group.

files_affected

Set of unique files affected by messages in this group.

key: str
severity: str | None = None
count: int = 0
messages: list
files_affected: set
add_message(message)

Add a message to this group.

Parameters:

message (Message) – The message to add.

Return type:

None

class sawmill.core.aggregation.SeverityStats(severity, total=0, by_id=<factory>)

Bases: object

Statistics for a severity level with breakdown by message ID.

Parameters:
severity

The severity level.

total

Total count of messages at this severity.

by_id

Breakdown of counts by message ID.

severity: str
total: int = 0
by_id: dict
class sawmill.core.aggregation.Aggregator(severity_levels, grouping_fields=None)

Bases: object

Aggregate messages for summary and grouped views.

This class provides methods to group messages by various fields and compute statistics for display. It must be configured with plugin-provided severity levels for proper severity ordering and styling.

Parameters:
severity_levels

List of severity level definitions from plugin. Required.

grouping_fields

List of grouping field definitions from plugin.

get_available_groupings()

Get list of field IDs available for grouping.

Return type:

list[str]

Returns:

List of field IDs that can be used with group_by().

get_grouping_field(field_id)

Get a grouping field definition by ID.

Parameters:

field_id (str) – The field identifier.

Return type:

GroupingField | None

Returns:

GroupingField if found, None otherwise.

get_severity_style(severity)

Get the display style for a severity level.

Parameters:

severity (str) – The severity level.

Return type:

str

Returns:

Rich style string from plugin, or empty string if not found.

get_severity_name(severity)

Get the display name for a severity level.

Parameters:

severity (str) – The severity level.

Return type:

str

Returns:

Display name, or title-cased severity if not found.

get_summary(messages)

Get summary statistics grouped by severity with ID breakdown.

Parameters:

messages (list[Message]) – List of messages to summarize.

Return type:

dict[str, SeverityStats]

Returns:

Dictionary mapping severity to SeverityStats.

group_by_field(messages, field_id)

Group messages by any field (builtin or metadata).

This is the generic grouping method that uses Message.get_field_value() to support both builtin fields and plugin-defined metadata fields.

Parameters:
  • messages (list[Message]) – List of messages to group.

  • field_id (str) – The field to group by.

Return type:

dict[str, MessageStats]

Returns:

Dictionary mapping field value to MessageStats.

group_by_severity(messages)

Group messages by severity level.

Parameters:

messages (list[Message]) – List of messages to group.

Return type:

dict[str, MessageStats]

Returns:

Dictionary mapping severity to MessageStats.

group_by_id(messages)

Group messages by message ID.

Parameters:

messages (list[Message]) – List of messages to group.

Return type:

dict[str, MessageStats]

Returns:

Dictionary mapping message ID to MessageStats.

group_by_file(messages)

Group messages by source file.

Messages without file references are grouped under “(no file)”.

Parameters:

messages (list[Message]) – List of messages to group.

Return type:

dict[str, MessageStats]

Returns:

Dictionary mapping file path to MessageStats.

group_by_category(messages)

Group messages by category.

Messages without categories are grouped under “(no category)”.

Parameters:

messages (list[Message]) – List of messages to group.

Return type:

dict[str, MessageStats]

Returns:

Dictionary mapping category to MessageStats.

group_by(messages, field)

Group messages by the specified field.

This method supports both builtin fields and plugin-defined metadata fields. For builtin fields, it uses optimized methods. For metadata fields, it uses the generic group_by_field() method.

Parameters:
  • messages (list[Message]) – List of messages to group.

  • field (str) – The field to group by.

Return type:

dict[str, MessageStats]

Returns:

Dictionary mapping field value to MessageStats.

Raises:

ValueError – If the field is not a builtin field and not in the plugin-provided grouping fields.

sorted_groups(groups, by_count=True)

Sort groups for display.

Parameters:
  • groups (dict[str, MessageStats]) – Dictionary of groups to sort.

  • by_count (bool) – If True, sort by count descending. If False, sort by key.

Return type:

list[tuple[str, MessageStats]]

Returns:

List of (key, stats) tuples in sorted order.

sorted_summary(summary)

Sort summary by severity order.

Uses plugin-provided severity levels if available, otherwise uses default ordering.

Parameters:

summary (dict[str, SeverityStats]) – Summary dictionary to sort.

Return type:

list[tuple[str, SeverityStats]]

Returns:

List of (severity, stats) tuples in severity order.

sawmill.core.waiver

Waiver loading, parsing, and matching for sawmill.

This module provides: - WaiverLoader: For reading TOML waiver files and validating waiver entries - WaiverMatcher: For matching log messages against waivers - WaiverGenerator: For generating waiver TOML from log messages

Waivers are for CI acceptance (pass/fail decisions with audit trail). They are distinct from suppressions which are for display filtering.

sawmill.core.waiver.waiver_to_toml(waiver)

Serialize a Waiver object to a [[waiver]] TOML block string.

Handles optional fields (content_match/pattern, expires, ticket).

Parameters:

waiver (Waiver) – The Waiver object to serialize.

Return type:

str

Returns:

A TOML string representing the waiver entry.

exception sawmill.core.waiver.WaiverValidationError(message, line=None, path=None, waiver_index=None)

Bases: Exception

Exception raised for waiver file validation errors.

Parameters:
  • message (str)

  • line (int | None)

  • path (Path | None)

  • waiver_index (int | None)

message

Error description

line

Line number where error occurred (if available)

path

Path to the waiver file (if available)

waiver_index

Index of the waiver entry with the error (if available)

class sawmill.core.waiver.WaiverLoader

Bases: object

Loader for sawmill TOML waiver files.

Waiver files use the following format:

[metadata] tool = “vivado” # Optional, for documentation

[[waiver]] message_id = “Vivado 12-3523” # Required: message ID to match content_match = “raw” # Optional: “raw” or “regex” content_pattern = “some text” # Optional: narrows match reason = “Intentional” # Required: why this is waived author = “user@email” # Required: who created this waiver date = “2026-01-18” # Required: when this was created expires = “2026-06-01” # Optional: expiration date ticket = “PROJ-123” # Optional: issue tracker reference

Example usage:

loader = WaiverLoader() waivers = loader.load(Path(“waivers.toml”))

VALID_CONTENT_MATCH = frozenset({'raw', 'regex'})
REQUIRED_FIELDS = frozenset({'author', 'date', 'message_id', 'reason'})
load(path)

Load waivers from a TOML file.

Parameters:

path (Path) – Path to the TOML waiver file

Return type:

WaiverFile

Returns:

WaiverFile instance with parsed waivers

Raises:
load_from_string(content, path=None)

Load waivers from a TOML string.

Parameters:
  • content (str) – TOML content as a string

  • path (Path | None) – Optional path for error reporting

Return type:

WaiverFile

Returns:

WaiverFile instance with parsed waivers

Raises:

WaiverValidationError – If the content contains invalid TOML or waiver entries with missing/invalid fields

class sawmill.core.waiver.WaiverMatcher(waivers)

Bases: object

Matches log messages against waivers.

The matcher checks messages against a list of waivers and returns the first matching waiver, or None if no waiver matches.

Matching is two-stage: 1. Match message_id exactly (required for all waivers) 2. If content_pattern is set, apply it as additional refinement:

  • content_match == “raw”: substring match on message content/raw_text

  • content_match == “regex”: regex search on message content/raw_text

  1. If no content_pattern: match all instances of this message_id

Example usage:

matcher = WaiverMatcher(waivers) waiver = matcher.is_waived(message) if waiver:

print(f”Message waived by: {waiver.reason}”)

Parameters:

waivers (list[Waiver])

property waivers: list[Waiver]

Get the list of waivers.

is_waived(message)

Check if a message is waived.

Matching logic: 1. Look up waivers by message_id (exact match) 2. For each matching waiver, check content_pattern if present 3. Waivers with content_pattern are checked first (more specific),

then waivers without (catch-all)

Parameters:

message (Message) – The Message to check against waivers

Return type:

Waiver | None

Returns:

The matching Waiver if found, or None

class sawmill.core.waiver.WaiverGenerator(author='<author>', reason='<reason - explain why this is acceptable>', severity_levels=None, min_waiver_level=1, include_all=False)

Bases: object

Generates waiver TOML from log messages.

This class creates valid waiver TOML files from a list of messages, suitable for CI acceptance workflows. Generated waivers include placeholder values for author and reason that users should review and update.

Generated waivers use message_id for all messages that have one. Messages without a message_id are skipped.

The generator filters messages by severity level. By default, only messages with level >= 1 (above the lowest informational level 0) are included. This behavior can be customized via the min_waiver_level parameter, or all messages can be included by setting include_all=True.

Example usage:

generator = WaiverGenerator(severity_levels=plugin_levels) toml_content = generator.generate(messages) print(toml_content) # Redirect to waivers.toml

Parameters:
generate(messages, tool=None)

Generate waiver TOML content from messages.

Only includes messages with severity level >= min_waiver_level (default 1, above informational level 0), unless include_all=True.

Parameters:
  • messages (list[Message]) – List of parsed log messages.

  • tool (str | None) – Optional tool name to include in metadata.

Return type:

str

Returns:

Valid TOML content as a string.

sawmill.core.config

Configuration loading and parsing for sawmill.

This module provides the ConfigLoader class for reading TOML configuration files and the Config dataclass for storing configuration values.

exception sawmill.core.config.ConfigError(message, line=None, path=None)

Bases: Exception

Exception raised for configuration parsing errors.

Parameters:
  • message (str)

  • line (int | None)

  • path (Path | None)

message

Error description

line

Line number where error occurred (if available)

path

Path to the config file (if available)

class sawmill.core.config.GeneralConfig(default_plugin=None)

Bases: object

General configuration settings.

Parameters:

default_plugin (str | None)

default_plugin: str | None = None
classmethod from_dict(data)

Create GeneralConfig from a dictionary.

Return type:

GeneralConfig

Parameters:

data (dict)

class sawmill.core.config.OutputConfig(color=True, format='text')

Bases: object

Output configuration settings.

Parameters:
color: bool = True
format: str = 'text'
classmethod from_dict(data)

Create OutputConfig from a dictionary.

Return type:

OutputConfig

Parameters:

data (dict)

class sawmill.core.config.Config(general=<factory>, output=<factory>)

Bases: object

Complete sawmill configuration.

Parameters:
general

General settings like default_plugin

output

Output settings like color and format

general: GeneralConfig
output: OutputConfig
classmethod from_dict(data)

Create Config from a dictionary.

Parameters:

data (dict) – Dictionary parsed from TOML file

Return type:

Config

Returns:

Config instance with values from dictionary

class sawmill.core.config.ConfigLoader

Bases: object

Loader for sawmill TOML configuration files.

Example usage:

loader = ConfigLoader() config = loader.load(Path(“sawmill.toml”))

# Or load defaults when no file exists config = loader.load(None)

# Or load resolved (user + local merge) config = loader.load_resolved()

load(path)

Load configuration from a TOML file.

Parameters:

path (Path | None) – Path to the TOML configuration file, or None to use defaults

Return type:

Config

Returns:

Config instance with values from file or defaults

Raises:
load_resolved(start_path=None)

Load and merge configuration from user and local config files.

Two-level merge: user config (~/.config/sawmill/config.toml) is the base, local config (<.sawmill|sawmill>/config.toml) overrides per-section.

Parameters:

start_path (Path | None) – Starting directory for local config discovery. If None, uses current working directory.

Return type:

Config

Returns:

Config instance with merged values from all sources.

Raises:

ConfigError – If any config file contains invalid TOML, or if both .sawmill/ and sawmill/ exist.

sawmill.core.suppress

Suppression file loading and saving for sawmill.

Suppressions are display filters (“hide this noise”), stored in .sawmill/suppress.toml as a [suppress] TOML table.

class sawmill.core.suppress.SuppressConfig(patterns=<factory>, message_ids=<factory>)

Bases: object

Suppression configuration settings.

Suppressions are for display filtering (hiding noise), distinct from waivers (CI acceptance with audit trail).

Parameters:
patterns: list[str]
message_ids: list[str]
classmethod from_dict(data)

Create SuppressConfig from a dictionary.

Return type:

SuppressConfig

Parameters:

data (dict)

class sawmill.core.suppress.SuppressLoader

Bases: object

Loader and saver for suppression TOML files.

load(path)

Read a suppress TOML file.

Parameters:

path (Path) – Path to the suppress.toml file.

Return type:

SuppressConfig

Returns:

SuppressConfig with values from the file, or empty defaults if the file doesn’t exist.

Raises:

ConfigError – If the file contains invalid TOML.

load_from_dir(sawmill_dir)

Load suppress.toml from a sawmill configuration directory.

Parameters:

sawmill_dir (Path) – Path to .sawmill/ or sawmill/ directory.

Return type:

SuppressConfig

Returns:

SuppressConfig from <dir>/suppress.toml, or defaults if missing.

save(config, path)

Write suppression config to a TOML file.

Parameters:
Return type:

None

sawmill.core.plugin

Plugin management for sawmill.

This module provides the PluginManager class that handles plugin discovery via Python entry points and registration with pluggy.

exception sawmill.core.plugin.PluginError

Bases: Exception

Base exception for plugin-related errors.

exception sawmill.core.plugin.PluginConflictError

Bases: PluginError

Raised when multiple plugins both claim high confidence for a file.

exception sawmill.core.plugin.NoPluginFoundError

Bases: PluginError

Raised when no plugin can handle a file.

class sawmill.core.plugin.PluginManager

Bases: object

Manages plugin discovery and registration.

This class handles: - Discovering plugins via Python entry points - Registering plugins with pluggy - Listing available plugins - Calling plugin hooks

Example

manager = PluginManager() manager.discover() # Find and register entry point plugins manager.register(MyPlugin()) # Manually register a plugin

# List available plugins for name in manager.list_plugins():

print(name)

# Call hooks results = manager.pm.hook.can_handle(path=Path(“test.log”))

register(plugin)

Register a plugin instance.

Parameters:

plugin (SawmillPlugin) – The plugin instance to register.

Return type:

None

unregister(name)

Unregister a plugin by name.

Parameters:

name (str) – The name of the plugin to unregister.

Return type:

None

discover()

Discover and register plugins from entry points.

Scans the ‘sawmill.plugins’ entry point group and registers any plugins found.

Return type:

list[str]

Returns:

List of discovered plugin names.

list_plugins()

List all registered plugin names.

Return type:

list[str]

Returns:

List of plugin names.

get_plugin(name)

Get a plugin by name.

Parameters:

name (str) – The name of the plugin.

Return type:

SawmillPlugin | None

Returns:

The plugin instance, or None if not found.

get_plugin_info(name)

Get information about a plugin.

Parameters:

name (str) – The name of the plugin.

Return type:

dict[str, str] | None

Returns:

Dictionary with plugin info (name, version, description), or None if not found.

auto_detect(path)

Automatically detect the appropriate plugin for a file.

Calls the can_handle hook on all registered plugins and selects the one with the highest confidence score.

Parameters:

path (Path) – Path to the log file to analyze.

Return type:

str

Returns:

Name of the plugin with highest confidence (>= 0.5).

Raises:
sawmill.core.plugin.select_plugin(manager, plugin_name, path)

Select the appropriate plugin for a log file.

If plugin_name is given, looks it up by name. Otherwise, auto-detects from the file.

Parameters:
  • manager (PluginManager) – The plugin manager with registered plugins.

  • plugin_name (str | None) – Explicit plugin name, or None for auto-detect.

  • path (Path) – Path to the log file.

Return type:

SawmillPlugin

Returns:

The selected plugin instance.

Raises:
sawmill.core.plugin.get_plugin_manager()

Create and configure the plugin manager.

Discovers plugins via entry points (including built-in plugins registered in pyproject.toml).

Return type:

PluginManager

Returns:

Configured PluginManager instance.

sawmill.core.severity

Severity-level helpers for sawmill.

Provides functions to query and compare severity levels from plugins. These are used by both the CLI and TUI layers.

sawmill.core.severity.get_severity_levels(plugin)

Get severity levels from plugin.

Parameters:

plugin – The plugin instance to get levels from.

Returns:

List of SeverityLevel objects.

Raises:

RuntimeError – If plugin doesn’t implement get_severity_levels().

sawmill.core.severity.get_severity_style_map(plugin)

Build a severity style map from plugin’s severity levels.

Parameters:

plugin – The plugin instance to get levels from.

Return type:

dict[str, str]

Returns:

Dictionary mapping severity ID to Rich style string.

sawmill.core.severity.get_severity_style(severity, style_map)

Get the Rich style for a given severity level.

Parameters:
  • severity (str | None) – The severity level.

  • style_map (dict[str, str]) – Dictionary mapping severity ID to style string.

Return type:

str

Returns:

Rich style string for the severity.

sawmill.core.severity.get_severity_level_map(plugin)

Build a severity level map from plugin’s severity levels.

Parameters:

plugin – The plugin instance to get levels from.

Return type:

dict[str, int]

Returns:

Dictionary mapping severity ID to level number.

sawmill.core.severity.severity_at_or_above(message_severity, min_severity, level_map)

Check if message severity is at or above the minimum level.

Parameters:
  • message_severity (str | None) – The message’s severity (may be None).

  • min_severity (str) – The minimum severity level to show.

  • level_map (dict[str, int]) – Dictionary mapping severity ID to level number.

Return type:

bool

Returns:

True if the message should be shown.