Skip to content

State & Config

biopro_sdk.plugin.state

Plugin state management for BioPro SDK.

Provides base class for plugin state that integrates with BioPro's undo/redo history system. Enables serialization and deserialization of plugin states for workflow persistence.

PluginState dataclass

Bases: ABC

Base state class for plugin analysis state.

Subclass this in your plugin and use @dataclass for automatic serialization. Enables undo/redo integration via BioPro's HistoryManager.

All fields in your state should be simple types (str, int, float, list, dict) to ensure proper serialization. Complex objects should be stored as paths or serializable representations.

This class strictly prevents dynamic attribute assignment. All attributes must be declared as fields in the subclass dataclass.

Source code in src/biopro_sdk/plugin/state.py
@dataclass
class PluginState(ABC):
    """Base state class for plugin analysis state.

    Subclass this in your plugin and use @dataclass for automatic serialization.
    Enables undo/redo integration via BioPro's HistoryManager.

    All fields in your state should be simple types (str, int, float, list, dict)
    to ensure proper serialization. Complex objects should be stored as paths
    or serializable representations.

    This class strictly prevents dynamic attribute assignment. All attributes
    must be declared as fields in the subclass dataclass.
    """

    def __setattr__(self, name: str, value: Any) -> None:
        fields = getattr(type(self), "__dataclass_fields__", {})
        if name not in fields and not hasattr(self, name):
            raise AttributeError(
                f"'{type(self).__name__}' object has no attribute '{name}'. "
                "Dynamic attribute assignment is strictly prohibited on PluginState objects."
            )
        super().__setattr__(name, value)

    def to_dict(self) -> dict[str, Any]:
        """Convert state to dictionary for serialization.

        Returns:
            Dictionary representation of the state
        """
        return asdict(self)

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> PluginState:
        """Reconstruct state from dictionary.

        Args:
            data: Dictionary previously produced by to_dict()

        Returns:
            New instance of this state class
        """
        return cls(**data)

from_dict(data) classmethod

Reconstruct state from dictionary.

Parameters:

Name Type Description Default
data dict[str, Any]

Dictionary previously produced by to_dict()

required

Returns:

Type Description
PluginState

New instance of this state class

Source code in src/biopro_sdk/plugin/state.py
@classmethod
def from_dict(cls, data: dict[str, Any]) -> PluginState:
    """Reconstruct state from dictionary.

    Args:
        data: Dictionary previously produced by to_dict()

    Returns:
        New instance of this state class
    """
    return cls(**data)

to_dict()

Convert state to dictionary for serialization.

Returns:

Type Description
dict[str, Any]

Dictionary representation of the state

Source code in src/biopro_sdk/plugin/state.py
def to_dict(self) -> dict[str, Any]:
    """Convert state to dictionary for serialization.

    Returns:
        Dictionary representation of the state
    """
    return asdict(self)

biopro_sdk.plugin.io

File I/O utilities for BioPro SDK.

Provides convenient wrappers for JSON serialization and configuration management for plugins.

PluginConfig

Bases: PreferenceManagerProtocol

Simple configuration management for plugins.

Stores settings in JSON in ~/.biopro/plugin_configs/{plugin_id}.json

This is useful for persisting user settings across sessions, like the last used parameters or paths.

Example

config = PluginConfig('my_plugin') config.set('threshold', 0.5) config.set('last_image_dir', '/path/to/images') threshold = config.get('threshold', default=0.0) config.save()

Source code in src/biopro_sdk/plugin/io.py
class PluginConfig(PreferenceManagerProtocol):
    """Simple configuration management for plugins.

    Stores settings in JSON in ~/.biopro/plugin_configs/{plugin_id}.json

    This is useful for persisting user settings across sessions, like
    the last used parameters or paths.

    Example:
        >>> config = PluginConfig('my_plugin')
        >>> config.set('threshold', 0.5)
        >>> config.set('last_image_dir', '/path/to/images')
        >>> threshold = config.get('threshold', default=0.0)
        >>> config.save()
    """

    def __init__(self, plugin_id: str):
        """Initialize config manager.

        Args:
            plugin_id: Unique plugin identifier (used for filename)
        """
        self.plugin_id = plugin_id
        self.config_dir = Path.home() / ".biopro" / "plugin_configs"
        self.config_file = self.config_dir / f"{plugin_id}.json"
        self.data: dict[str, Any] = {}
        self.load()

    def load(self) -> None:
        """Load config from disk.

        Called automatically in __init__. Call again to reload from disk.
        """
        if self.config_file.exists():
            try:
                self.data = load_json(str(self.config_file))
            except Exception as e:
                logger.warning(f"Failed to load config for {self.plugin_id}: {e}")
                self.data = {}
        else:
            self.data = {}

    def save(self) -> None:
        """Save config to disk.

        Raises:
            IOError: If file cannot be written
        """
        try:
            save_json(str(self.config_file), self.data)
        except Exception as e:
            logger.error(f"Failed to save config for {self.plugin_id}: {e}")

    def set(self, key: str, value: Any) -> None:
        """Set a configuration value.

        Args:
            key: Configuration key
            value: Configuration value (should be JSON-serializable)
        """
        self.data[key] = value

    def get(self, key: str, default: Any = None) -> Any:
        """Get a configuration value.

        Args:
            key: Configuration key
            default: Value to return if key doesn't exist

        Returns:
            Configuration value or default
        """
        return self.data.get(key, default)

    def has(self, key: str) -> bool:
        """Check if a key exists.

        Args:
            key: Configuration key

        Returns:
            True if key exists
        """
        return key in self.data

    def clear(self) -> None:
        """Clear all config values (does not delete file until save())."""
        self.data.clear()

    def __getitem__(self, key: str):
        """Get value using dictionary syntax."""
        return self.data[key]

    def __setitem__(self, key: str, value: Any):
        """Set value using dictionary syntax."""
        self.data[key] = value

__getitem__(key)

Get value using dictionary syntax.

Source code in src/biopro_sdk/plugin/io.py
def __getitem__(self, key: str):
    """Get value using dictionary syntax."""
    return self.data[key]

__init__(plugin_id)

Initialize config manager.

Parameters:

Name Type Description Default
plugin_id str

Unique plugin identifier (used for filename)

required
Source code in src/biopro_sdk/plugin/io.py
def __init__(self, plugin_id: str):
    """Initialize config manager.

    Args:
        plugin_id: Unique plugin identifier (used for filename)
    """
    self.plugin_id = plugin_id
    self.config_dir = Path.home() / ".biopro" / "plugin_configs"
    self.config_file = self.config_dir / f"{plugin_id}.json"
    self.data: dict[str, Any] = {}
    self.load()

__setitem__(key, value)

Set value using dictionary syntax.

Source code in src/biopro_sdk/plugin/io.py
def __setitem__(self, key: str, value: Any):
    """Set value using dictionary syntax."""
    self.data[key] = value

clear()

Clear all config values (does not delete file until save()).

Source code in src/biopro_sdk/plugin/io.py
def clear(self) -> None:
    """Clear all config values (does not delete file until save())."""
    self.data.clear()

get(key, default=None)

Get a configuration value.

Parameters:

Name Type Description Default
key str

Configuration key

required
default Any

Value to return if key doesn't exist

None

Returns:

Type Description
Any

Configuration value or default

Source code in src/biopro_sdk/plugin/io.py
def get(self, key: str, default: Any = None) -> Any:
    """Get a configuration value.

    Args:
        key: Configuration key
        default: Value to return if key doesn't exist

    Returns:
        Configuration value or default
    """
    return self.data.get(key, default)

has(key)

Check if a key exists.

Parameters:

Name Type Description Default
key str

Configuration key

required

Returns:

Type Description
bool

True if key exists

Source code in src/biopro_sdk/plugin/io.py
def has(self, key: str) -> bool:
    """Check if a key exists.

    Args:
        key: Configuration key

    Returns:
        True if key exists
    """
    return key in self.data

load()

Load config from disk.

Called automatically in init. Call again to reload from disk.

Source code in src/biopro_sdk/plugin/io.py
def load(self) -> None:
    """Load config from disk.

    Called automatically in __init__. Call again to reload from disk.
    """
    if self.config_file.exists():
        try:
            self.data = load_json(str(self.config_file))
        except Exception as e:
            logger.warning(f"Failed to load config for {self.plugin_id}: {e}")
            self.data = {}
    else:
        self.data = {}

save()

Save config to disk.

Raises:

Type Description
IOError

If file cannot be written

Source code in src/biopro_sdk/plugin/io.py
def save(self) -> None:
    """Save config to disk.

    Raises:
        IOError: If file cannot be written
    """
    try:
        save_json(str(self.config_file), self.data)
    except Exception as e:
        logger.error(f"Failed to save config for {self.plugin_id}: {e}")

set(key, value)

Set a configuration value.

Parameters:

Name Type Description Default
key str

Configuration key

required
value Any

Configuration value (should be JSON-serializable)

required
Source code in src/biopro_sdk/plugin/io.py
def set(self, key: str, value: Any) -> None:
    """Set a configuration value.

    Args:
        key: Configuration key
        value: Configuration value (should be JSON-serializable)
    """
    self.data[key] = value

get_plugin_logger(plugin_id)

Get a logger for a plugin.

Parameters:

Name Type Description Default
plugin_id str

Plugin identifier

required

Returns:

Type Description
Logger

Configured logger with plugin name prefixed to 'biopro.plugins'

Example

logger = get_plugin_logger("my_plugin") logger.info("Plugin initialized") # Logs to "biopro.plugins.my_plugin"

Source code in src/biopro_sdk/plugin/io.py
def get_plugin_logger(plugin_id: str) -> logging.Logger:
    """Get a logger for a plugin.

    Args:
        plugin_id: Plugin identifier

    Returns:
        Configured logger with plugin name prefixed to 'biopro.plugins'

    Example:
        >>> logger = get_plugin_logger("my_plugin")
        >>> logger.info("Plugin initialized")  # Logs to "biopro.plugins.my_plugin"
    """
    return logging.getLogger(f"biopro.plugins.{plugin_id}")

load_json(path)

Load JSON from file.

Parameters:

Name Type Description Default
path str

File path

required

Returns:

Type Description
dict[str, Any]

Parsed JSON dictionary

Raises:

Type Description
FileNotFoundError

If file doesn't exist

JSONDecodeError

If JSON is invalid

Source code in src/biopro_sdk/plugin/io.py
def load_json(path: str) -> dict[str, Any]:
    """Load JSON from file.

    Args:
        path: File path

    Returns:
        Parsed JSON dictionary

    Raises:
        FileNotFoundError: If file doesn't exist
        json.JSONDecodeError: If JSON is invalid
    """
    with open(path) as f:
        return json.load(f)

read_binary(path)

Read binary data from a file.

Source code in src/biopro_sdk/plugin/io.py
def read_binary(path: Path | str) -> bytes:
    """Read binary data from a file."""
    with open(path, "rb") as f:
        return f.read()

save_json(path, data, pretty=True)

Save JSON to file.

Parameters:

Name Type Description Default
path str

File path

required
data dict[str, Any]

Dictionary to save

required
pretty bool

If True, indent for readability (2 spaces)

True
Source code in src/biopro_sdk/plugin/io.py
def save_json(path: str, data: dict[str, Any], pretty: bool = True) -> None:
    """Save JSON to file.

    Args:
        path: File path
        data: Dictionary to save
        pretty: If True, indent for readability (2 spaces)
    """
    Path(path).parent.mkdir(parents=True, exist_ok=True)
    with open(path, "w") as f:
        json.dump(data, f, indent=2 if pretty else None)

write_binary(path, data)

Write binary data to a file.

Source code in src/biopro_sdk/plugin/io.py
def write_binary(path: Path | str, data: bytes) -> None:
    """Write binary data to a file."""
    Path(path).parent.mkdir(parents=True, exist_ok=True)
    with open(path, "wb") as f:
        f.write(data)

biopro_sdk.plugin.preferences

Preference Manager Interface for BioPro SDK.

PreferenceManagerProtocol

Bases: Protocol

Standardized interface for preference management.

This interface ensures that both the Core application and SDK plugins have a unified way to interact with preferences, adhering to the Dependency Inversion Principle (DIP).

Source code in src/biopro_sdk/plugin/preferences.py
@runtime_checkable
class PreferenceManagerProtocol(Protocol):
    """Standardized interface for preference management.

    This interface ensures that both the Core application and SDK plugins
    have a unified way to interact with preferences, adhering to the
    Dependency Inversion Principle (DIP).
    """

    def load(self) -> None:
        """Load preferences from storage."""
        ...

    def save(self) -> None:
        """Save preferences to storage."""
        ...

    def set(self, key: str, value: Any) -> None:
        """Set a configuration value."""
        ...

    def get(self, key: str, default: Any = None) -> Any:
        """Get a configuration value."""
        ...

    def has(self, key: str) -> bool:
        """Check if a key exists."""
        ...

    def clear(self) -> None:
        """Clear all preference values."""
        ...

clear()

Clear all preference values.

Source code in src/biopro_sdk/plugin/preferences.py
def clear(self) -> None:
    """Clear all preference values."""
    ...

get(key, default=None)

Get a configuration value.

Source code in src/biopro_sdk/plugin/preferences.py
def get(self, key: str, default: Any = None) -> Any:
    """Get a configuration value."""
    ...

has(key)

Check if a key exists.

Source code in src/biopro_sdk/plugin/preferences.py
def has(self, key: str) -> bool:
    """Check if a key exists."""
    ...

load()

Load preferences from storage.

Source code in src/biopro_sdk/plugin/preferences.py
def load(self) -> None:
    """Load preferences from storage."""
    ...

save()

Save preferences to storage.

Source code in src/biopro_sdk/plugin/preferences.py
def save(self) -> None:
    """Save preferences to storage."""
    ...

set(key, value)

Set a configuration value.

Source code in src/biopro_sdk/plugin/preferences.py
def set(self, key: str, value: Any) -> None:
    """Set a configuration value."""
    ...