Adapters
The adapters module wraps external tools and APIs behind consistent Python interfaces.
Overview
Wrap subprocess calls, COM automation, or REST APIs.
ShellAdapter.exe() returns (stderr, stdout) tuples for standard shell operations.
UvToolManager.run() exits the process directly after execution.
Handle platform-specific differences internally.
Log operations via the standard logging module.
Adapters differ in how they return results: shell wrappers yield (stderr, stdout) for post-processing, while UvToolManager terminates the process as part of its flow.
Return Convention
stderr, stdout = adapter.exe("command", working_dir)
if stderr:
# Handle error
# Process stdout
When to Use
Choosing the Right Adapter
Scenario |
Adapter |
Rationale |
|---|---|---|
New projects, fast installs |
UvAdapter / UvToolManager |
uv is faster than Poetry, better for CI/CD |
Running arbitrary shell commands |
ShellAdapter |
Handles aliases, env vars, logging |
JIRA issue creation |
JiraAdapter |
Typed DTO, handles custom field quirks |
Windows Outlook calendar |
OutlookLocalAdapter |
COM automation for local Outlook |
Styled terminal output |
ConsoleAdapter |
Rich formatting, spinners, confirmations |
ShellAdapter vs Specific Adapters
Specific Adapters (JiraAdapter, OutlookLocalAdapter) |
ShellAdapter |
|---|---|
Type-safe interfaces via DTOs |
One-off commands without dedicated adapters |
API-specific error handling |
Scripts requiring alias expansion |
Protocol abstraction (REST, COM) |
Commands needing env var interpolation |
API Reference
ShellAdapter
Subprocess execution with alias and environment variable expansion.
- class buvis.pybase.adapters.ShellAdapter(*, suppress_logging: bool = False)
Bases:
objectA class for executing shell commands and logging their output, with support for command aliases and environment variables.
It executes shell commands, logs the output, and handles errors.
- alias(alias: str, command: str) None
Define a command alias.
- Parameters:
alias – The alias name to define.
command – The command string that the alias maps to.
- exe(command: str, working_dir: Path | None) tuple[str, str]
Execute a shell command and log its output.
This method runs the given command, logs stdout as info and stderr as error. If the command fails, it logs both stdout and stderr.
- Parameters:
command – A string representing the command to execute.
working_dir – Path to directory where the command will be executed.
- Returns:
A tuple of (stderr, stdout) from command execution. On failure, returns (error_message, empty string).
- interact(command: str, prompt: str, working_dir: Path | None) None
- is_command_available(command: str) bool
Check if a given command is available in the system PATH.
- Parameters:
command – The name of the command to check.
- Returns:
True if the command exists, False otherwise.
ConsoleAdapter
Rich console output wrapper for styled terminal messages.
- class buvis.pybase.adapters.console.console.ConsoleAdapter
Bases:
objectRich console wrapper for styled terminal output.
Example
>>> from buvis.pybase.adapters import ConsoleAdapter >>> console = ConsoleAdapter() >>> console.success("Operation completed")
Checkmark, warning, and failure flows reuse the CHECKMARK, WARNING, and CROSSMARK markers together with STYLE_SUCCESS_MSG, STYLE_WARNING_MSG, and STYLE_FAILURE_MSG so decorated messages remain consistent.
- capture() Capture
Return a Rich Capture context manager for console output.
- Returns:
Context manager that captures console output.
- Return type:
Capture
- confirm(question: str) bool
Prompt the user for confirmation via Rich Confirm.ask.
- Parameters:
question (str) – Prompt text presented to the user.
- Returns:
Result of the confirmation.
- Return type:
bool
- failure(message: str, details: str | None = None) None
Print a red failure message, optionally including details.
- Parameters:
message (str) – Text to display.
details (str | None) – Additional information to display after the failure message.
- format_failure(message: str, details: str | None = None) str
Return a red crossmark Rich markup string with optional details.
- Parameters:
message (str) – Text to format.
details (str | None) – Additional detail text to append.
- Returns:
Rich markup string with crossmark and red styling.
- Return type:
str
- format_success(message: str) str
Return a green checkmark Rich markup string.
- Parameters:
message (str) – Text to format.
- Returns:
Rich markup string with checkmark and green styling.
- Return type:
str
- format_warning(message: str) str
Return an orange warning symbol Rich markup string.
- Parameters:
message (str) – Text to format.
- Returns:
Rich markup string with warning symbol and orange styling.
- Return type:
str
- nl() None
Output an empty line to the console.
- panic(message: str, details: str | None = None) None
Print a failure message and exit the program.
- Parameters:
message (str) – Text to display.
details (str | None) – Additional information to display after the failure message.
Note
Terminates the program by calling sys.exit().
- print(message: str, *, mode: str = 'normal') None
Render text through the console with optional rendering modes.
- Parameters:
message (str) – Content to print.
mode (str) – Rendering mode, one of
normal,raw, ormarkdown_with_frontmatter.
- print_side_by_side(title_left: str, text_left: str, title_right: str, text_right: str, *, mode_left: str = 'normal', mode_right: str = 'normal') None
Render two panels in a half-width, side-by-side layout where each panel uses half the console width to keep columns balanced.
- Parameters:
title_left (str) – Panel title for the left column.
text_left (str) – Content for the left column body.
title_right (str) – Panel title for the right column.
text_right (str) – Content for the right column body.
mode_left (str) – Rendering mode for the left column content.
mode_right (str) – Rendering mode for the right column content.
Example
>>> console.print_side_by_side( ... title_left="Config", ... text_left="name: buvis\n---\n# details", ... title_right="Result", ... text_right="Success", ... mode_left="markdown_with_frontmatter", ... mode_right="raw", ... )
- status(message: str) Status
Return a Rich Status context manager with arrow3 spinner.
- Parameters:
message (str) – Text to display while the status context is active.
- Returns:
Context manager for long-running operations.
- Return type:
Status
- success(message: str) None
Print a green checkmark status message.
- Parameters:
message (str) – Text to display.
- warning(message: str) None
Print an orange warning status message.
- Parameters:
message (str) – Text to display.
console singleton instance exposes the adapter for quick access.
- buvis.pybase.adapters.logging_to_console(*, show_level: bool = True, show_time: bool = False, show_path: bool = False) Generator[None, None, None]
Context manager that routes logging output through the Rich console.
Temporarily adds a
CapturingRichHandlerto the root logger, setting the logger level toINFOwhile the context is active and restoring the previous handler and level on exit.- Parameters:
show_level (bool) – Include the log level in the captured output. Defaults to
True.show_time (bool) – Include timestamps in the captured output. Defaults to
False.show_path (bool) – Include the logger path in the captured output. Defaults to
False.
- Yields:
None – Logging is redirected to the Rich console for the duration of the context.
Example
>>> with logging_to_console(): ... logging.info("Hello Rich")
UvAdapter
Fast Python package manager integration with auto-installation.
- class buvis.pybase.adapters.UvAdapter
Bases:
objectuv package manager integration.
Provides auto-installation of uv and PATH configuration for cross-platform Python tooling.
- static ensure_uv() None
Ensure uv is installed and available in PATH.
If uv is not found, automatically installs it using: - Windows: PowerShell installer from astral.sh - Unix: curl installer from astral.sh
Updates PATH to include common uv installation directories: - ~/.cargo/bin - ~/.local/bin (Unix) - ~/AppData/Local/uv (Windows)
Exits with code 1 if installation fails.
UvToolManager
Manage and run CLI tools installed via uv.
- class buvis.pybase.adapters.UvToolManager
Bases:
objectManage and run Python tools via uv.
Supports development workflows with automatic tool discovery and installation from local project sources.
- static install_all(scripts_root: Path | None = None) None
Install all projects in src/ as uv tools.
Scans scripts_root/src/ for directories containing pyproject.toml and installs each as a uv tool.
- Parameters:
scripts_root – Root directory containing src/. Defaults to cwd.
- static install_tool(project_path: Path) None
Install a project as a uv tool.
Uses –force –upgrade to ensure latest version. On failure, cleans the tool cache and retries once.
- Parameters:
project_path – Directory containing pyproject.toml.
- classmethod run(script_path: str, args: list[str] | None = None) None
Run a tool from local venv, project source, or installed uv tool.
- Execution priority when BUVIS_DEV_MODE=1:
Local .venv/bin/{tool} if exists
uv run from project source (src/{pkg}/pyproject.toml)
Exit with error if neither found
- Execution priority when BUVIS_DEV_MODE unset:
uv tool run {tool}
Auto-install from local source if tool not found
Exit with error if no source available
- Parameters:
script_path – Path to the launcher script (used to derive tool name).
args – Command arguments. Defaults to sys.argv[1:].
Note
Tool name derived from script stem: my-tool -> pkg my_tool.
JiraAdapter
JIRA REST API adapter for issue creation.
- class buvis.pybase.adapters.JiraAdapter(settings: JiraSettings)
Bases:
objectJIRA REST API adapter for issue operations.
- Requirements:
Provide a populated JiraSettings instance with server and token.
- Optional:
Configure proxy in the settings to route requests through a proxy server.
Example
>>> settings = JiraSettings(server="https://jira", token="abc123") >>> jira = JiraAdapter(settings) >>> issue = JiraIssueDTO(...) >>> created = jira.create(issue) >>> print(created.link) >>> jira.get(created.id) >>> results = jira.search("project = PROJ ORDER BY created DESC", max_results=5) >>> jira.transition(created.id, "Start Progress")
- add_comment(issue_key: str, body: str, is_internal: bool = False) JiraCommentDTO
Add comment to issue.
- Parameters:
issue_key – JIRA issue key.
body – Comment text content.
is_internal – If True, restrict visibility to Administrators.
- Returns:
JiraCommentDTO for the created comment.
- Raises:
JiraNotFoundError – Issue does not exist.
- create(issue: JiraIssueDTO) JiraIssueDTO
Create a JIRA issue via the REST API.
- Parameters:
issue (JiraIssueDTO) – containing all required fields.
- Returns:
populated with server-assigned id and link.
- Return type:
- Custom Field Mappings:
Determined by self._settings.field_mappings. Defaults: ticket -> customfield_11502, team -> customfield_10501, feature -> customfield_10001, region -> customfield_12900.
Note
Custom fields customfield_10001 (feature) and customfield_12900 (region) require post-creation update due to JIRA API limitations.
- get(issue_key: str) JiraIssueDTO
Retrieve issue by key.
- Parameters:
issue_key – JIRA issue key (e.g., “PROJ-123”).
- Returns:
JiraIssueDTO with issue data.
- Raises:
JiraNotFoundError – Issue does not exist.
- get_comments(issue_key: str) list[JiraCommentDTO]
Get all comments on issue.
- Parameters:
issue_key – JIRA issue key.
- Returns:
List of JiraCommentDTO, chronologically ordered.
- Raises:
JiraNotFoundError – Issue does not exist.
- get_link_types() list[str]
Get available issue link types.
- Returns:
List of link type names (e.g., “Blocks”, “Duplicates”).
- Raises:
JIRAError – JIRA API call failed.
- get_transitions(issue_key: str) list[dict[str, str]]
List available transitions for issue.
- Parameters:
issue_key – JIRA issue key.
- Returns:
List of dicts with “id” and “name” keys.
- Raises:
JiraNotFoundError – Issue does not exist.
- link_issues(from_key: str, to_key: str, link_type: str) None
Create link between issues.
- Parameters:
from_key – Source issue key (outward side of link).
to_key – Target issue key (inward side of link).
link_type – Link type name (e.g., “Blocks”, “Duplicates”).
- Raises:
JiraNotFoundError – Either issue does not exist.
JiraLinkError – Link creation failed.
- search(jql: str, start_at: int = 0, max_results: int = 50, fields: str | None = None) JiraSearchResult
Execute JQL query with pagination.
- Parameters:
jql – JQL query string.
start_at – Index of first result (for pagination).
max_results – Maximum results to return.
fields – Comma-separated field names to include, or None for all.
- Returns:
JiraSearchResult with matching issues and pagination info.
- transition(issue_key: str, transition: str, fields: dict[str, Any] | None = None, comment: str | None = None) None
Execute workflow transition.
- Parameters:
issue_key – JIRA issue key.
transition – Transition ID or name.
fields – Optional field updates during transition.
comment – Optional comment to add during transition.
- Raises:
JiraNotFoundError – Issue does not exist.
JiraTransitionError – Transition unavailable.
- update(issue_key: str, fields: dict[str, Any]) JiraIssueDTO
Update issue fields.
- Parameters:
issue_key – Issue to update.
fields – Dict of field names to new values.
- Returns:
Updated JiraIssueDTO.
- Raises:
JiraNotFoundError – Issue does not exist.
- class buvis.pybase.adapters.jira.domain.JiraIssueDTO(project: str, title: str, description: str, issue_type: str, labels: list[str], priority: str, ticket: str, feature: str, assignee: str, reporter: str, team: str | None, region: str | None, id: str | None = None, link: str | None = None)
Bases:
objectDTO for JIRA issue creation.
Maps Python fields to JIRA API field names. Custom field IDs are configured via JiraSettings.field_mappings.
- project
JIRA project key (e.g., ‘BUV’).
- Type:
str
- title
Issue summary text, maps to JIRA ‘summary’.
- Type:
str
- description
Issue body text.
- Type:
str
- issue_type
Type name (e.g., ‘Task’, ‘Bug’).
- Type:
str
- labels
List of label strings.
- Type:
list[str]
- priority
Priority name (e.g., ‘Medium’, ‘High’).
- Type:
str
- ticket
Parent ticket reference (custom field).
- Type:
str
- feature
Feature/epic link (custom field).
- Type:
str
- assignee
Assignee username key.
- Type:
str
- reporter
Reporter username key.
- Type:
str
- team
Team name (custom field), None if not set.
- Type:
str | None
- region
Region value (custom field), None if not set.
- Type:
str | None
- id
Server-assigned issue key (e.g., ‘BUV-123’), populated after creation.
- Type:
str | None
- link
Permalink URL, populated after creation.
- Type:
str | None
Platform-Specific Adapters
OutlookLocalAdapter
Windows-only adapter for local Outlook COM automation. Requires pywin32
and a local Outlook installation. Only available when os.name == "nt".
Note
This adapter cannot be autodocumented cross-platform as it raises OSError
on import when os.name != "nt".
Methods:
__init__(): Connect to local Outlook via COM. Initializes MAPI namespace and default calendar.create_timeblock(appointment_input: dict): Create a calendar appointment. Keys:subject,body,duration(minutes),location,categories,start(optional datetime).get_all_appointments(): Retrieve all calendar appointments sorted by start time.get_day_appointments(appointments, date): Filter appointments to a single day.get_conflicting_appointment(desired_start, desired_duration, debug_level=0): Find appointment conflicting with proposed time slot.
Example:
# Windows only
from buvis.pybase.adapters import OutlookLocalAdapter
adapter = OutlookLocalAdapter()
adapter.create_timeblock({
"subject": "Check-in",
"body": "Daily sync",
"duration": 30,
"location": "Desk",
"categories": "Work"
})
Examples
ShellAdapter Example
from pathlib import Path
from buvis.pybase.adapters import ShellAdapter
shell = ShellAdapter()
shell.alias("lint", "poetry run lint")
if not shell.is_command_available("git"):
raise RuntimeError("git is required for this workflow")
stderr, stdout = shell.exe("lint", Path("src"))
if stderr:
raise RuntimeError(f"lint failed: {stderr.strip()}")
print(stdout)
ConsoleAdapter Example
from buvis.pybase.adapters.console.console import console
with console.status("Deploying service"):
console.print("Gathering assets")
if not console.confirm("Continue deployment?"):
console.failure("Deployment aborted by user")
raise SystemExit(1)
console.success("Deployment finished")
JiraAdapter Example
Ensure configuration exposes server and token before instantiating the adapter.
from jira.exceptions import JIRAError
from buvis.pybase.adapters import JiraAdapter
from buvis.pybase.adapters.jira.domain import JiraIssueDTO
cfg = MyConfig() # supplies server, token, and optional proxy
jira = JiraAdapter(cfg)
issue = JiraIssueDTO(
project="BUV",
title="Document new adapter patterns",
description="Describe adapter usage in docs",
issue_type="Task",
labels=["docs", "pybase"],
priority="Medium",
ticket="BUV-123",
feature="adapter-workflows",
assignee="alice",
reporter="bob",
team="platform",
region="emea",
)
try:
created = jira.create(issue)
print(f"Issue created: {created.id} -> {created.link}")
except JIRAError as e:
print(f"JIRA API error: {e.status_code} - {e.text}")
raise
UvAdapter Example
from buvis.pybase.adapters import UvAdapter
# Ensure uv is installed and PATH is configured
UvAdapter.ensure_uv()
# uv is now available for subprocess calls
# Use UvToolManager for running uv-managed tools
UvToolManager Example
from pathlib import Path
from buvis.pybase.adapters import UvToolManager
project_root = Path("/project")
# Directory layout:
# /project
# ├── bin/
# └── src/
# └── my_tool/
UvToolManager.install_all(project_root)
UvToolManager.install_tool(project_root / "src" / "my_tool")
UvToolManager.run(project_root / "bin" / "my-tool", ["--help"]) # exits on completion