Skip to content

dry_run

Dry Run Module.

This module provides a dry run simulation for cleaning and genre updates. It defines classes that can be used by the main application to simulate AppleScript interactions and processing logic without modifying the actual music library.

DryRunAppleScriptClient

DryRunAppleScriptClient(
    real_client, config, console_logger, error_logger
)

Bases: AppleScriptClientProtocol

AppleScript client that logs actions instead of modifying the library.

Initialize the DryRunAppleScriptClient with dependencies.

Parameters:

Name Type Description Default
real_client AppleScriptClientProtocol

The real AppleScript client to delegate fetch operations to

required
config AppConfig

Typed application configuration

required
console_logger Logger

Logger for console output

required
error_logger Logger

Logger for error output

required
Source code in src/core/dry_run.py
def __init__(
    self,
    real_client: AppleScriptClientProtocol,
    config: AppConfig,
    console_logger: logging.Logger,
    error_logger: logging.Logger,
) -> None:
    """Initialize the DryRunAppleScriptClient with dependencies.

    Args:
        real_client: The real AppleScript client to delegate fetch operations to
        config: Typed application configuration
        console_logger: Logger for console output
        error_logger: Logger for error output

    """
    self._real_client = real_client
    self.console_logger = console_logger
    self.error_logger = error_logger
    self.config = config
    self.actions: list[dict[str, Any]] = []
    self.apple_scripts_dir: str = config.apple_scripts_dir

initialize async

initialize()

Initialize the DryRunAppleScriptClient.

Delegates initialization to the underlying real client to ensure all fetch operations work correctly in dry-run mode.

Source code in src/core/dry_run.py
async def initialize(self) -> None:
    """Initialize the DryRunAppleScriptClient.

    Delegates initialization to the underlying real client to ensure
    all fetch operations work correctly in dry-run mode.

    """
    await self._real_client.initialize()

run_script async

run_script(
    script_name,
    arguments=None,
    timeout=None,
    context_artist=None,
    context_album=None,
    context_track=None,
    label=None,
)

Run an AppleScript by name in dry run mode.

For 'fetch' operations, delegates to the real client to get actual tracks. For update operations, log the action without making any changes.

Parameters:

Name Type Description Default
script_name str

Name of the AppleScript to run.

required
arguments list[str] | None

List of arguments to pass to the script.

None
timeout float | None

Optional timeout in seconds.

None
context_artist str | None

Artist name for contextual logging (optional).

None
context_album str | None

Album name for contextual logging (optional).

None
context_track str | None

Track name for contextual logging (optional).

None
label str | None

Custom label for logging (defaults to script_name).

None

Returns:

Type Description
str | None

str | None: The script output if this is a fetch operation, DRY_RUN_SUCCESS_MESSAGE otherwise.

Source code in src/core/dry_run.py
async def run_script(
    self,
    script_name: str,
    arguments: list[str] | None = None,
    timeout: float | None = None,
    context_artist: str | None = None,
    context_album: str | None = None,
    context_track: str | None = None,
    label: str | None = None,
) -> str | None:
    """Run an AppleScript by name in dry run mode.

    For 'fetch' operations, delegates to the real client to get actual tracks.
    For update operations, log the action without making any changes.

    Args:
        script_name: Name of the AppleScript to run.
        arguments: List of arguments to pass to the script.
        timeout: Optional timeout in seconds.
        context_artist: Artist name for contextual logging (optional).
        context_album: Album name for contextual logging (optional).
        context_track: Track name for contextual logging (optional).
        label: Custom label for logging (defaults to script_name).

    Returns:
        str | None: The script output if this is a fetch operation, DRY_RUN_SUCCESS_MESSAGE otherwise.

    """
    if isinstance(script_name, str) and script_name.startswith("fetch"):
        # For fetch operations, we need REAL data from Music.app
        # Apply test_artists filter if configured
        test_artists = self.config.development.test_artists

        # If test_artists is configured, and we're fetching all tracks (no args),
        # this is handled by the higher-level music_updater logic
        # Don't override here - let the calling code handle multiple test artists
        if test_artists and (not arguments or not arguments[0]):
            self.console_logger.info(
                "DRY-RUN: Test artists configured: %s (handled by caller)",
                test_artists,
            )
            # Don't modify arguments - let caller handle test artist iteration

        # Delegate to real client to fetch actual tracks
        result = await self._real_client.run_script(
            script_name,
            arguments,
            timeout,
            context_artist=context_artist,
            context_album=context_album,
            context_track=context_track,
            label=label,
        )
        return str(result) if result is not None else None

    # Log the dry run action without actually executing it
    self.console_logger.info(
        "DRY-RUN: Would run %s with args: %s",
        script_name,
        arguments or [],
    )
    self.actions.append({"script": script_name, "args": arguments or []})
    return str(DRY_RUN_SUCCESS_MESSAGE)

fetch_tracks_by_ids async

fetch_tracks_by_ids(
    track_ids, batch_size=1000, timeout=None
)

Fetch tracks by IDs - delegates to real client in dry-run mode.

Parameters:

Name Type Description Default
track_ids list[str]

List of track IDs to fetch

required
batch_size int

Maximum number of IDs per batch (default: 1000)

1000
timeout float | None

Timeout in seconds for script execution

None

Returns:

Type Description
list[dict[str, str]]

List of track dictionaries with metadata

Source code in src/core/dry_run.py
async def fetch_tracks_by_ids(
    self,
    track_ids: list[str],
    batch_size: int = 1000,
    timeout: float | None = None,
) -> list[dict[str, str]]:
    """Fetch tracks by IDs - delegates to real client in dry-run mode.

    Args:
        track_ids: List of track IDs to fetch
        batch_size: Maximum number of IDs per batch (default: 1000)
        timeout: Timeout in seconds for script execution

    Returns:
        List of track dictionaries with metadata

    """
    self.console_logger.info("DRY-RUN: Fetching %d tracks by ID (delegating to real client)", len(track_ids))
    return await self._real_client.fetch_tracks_by_ids(track_ids, batch_size=batch_size, timeout=timeout)

fetch_all_track_ids async

fetch_all_track_ids(timeout=None)

Fetch all track IDs - delegates to real client in dry-run mode.

Parameters:

Name Type Description Default
timeout float | None

Timeout in seconds for script execution

None

Returns:

Type Description
list[str]

List of track ID strings

Source code in src/core/dry_run.py
async def fetch_all_track_ids(self, timeout: float | None = None) -> list[str]:
    """Fetch all track IDs - delegates to real client in dry-run mode.

    Args:
        timeout: Timeout in seconds for script execution

    Returns:
        List of track ID strings

    """
    self.console_logger.info("DRY-RUN: Fetching all track IDs (delegating to real client)")
    return await self._real_client.fetch_all_track_ids(timeout=timeout)

get_actions

get_actions()

Get the list of actions recorded during the dry run.

Returns:

Type Description
list[dict[str, Any]]

List of dictionaries, each containing:

list[dict[str, Any]]
  • "script": Name of the AppleScript that would have been executed
list[dict[str, Any]]
  • "args": List of arguments that would have been passed
Source code in src/core/dry_run.py
def get_actions(self) -> list[dict[str, Any]]:
    """Get the list of actions recorded during the dry run.

    Returns:
        List of dictionaries, each containing:
        - "script": Name of the AppleScript that would have been executed
        - "args": List of arguments that would have been passed

    """
    return self.actions