Skip to main content

No project description provided

Project description

OaaS-SDK2

This library helps you develop a runtime that can be run in a Object as a Service (OaaS) serverless platform. For more information on the OaaS model, visit https://github.com/hpcclab/OaaS.

Table of Contents

Documentation

For a comprehensive guide and API reference, please see the docs directory:

  • Tutorial: A step-by-step guide to getting started with the OaaS SDK.
  • API Reference: A detailed reference of all classes, methods, and functions.

Installation

To install oaas-sdk2-py, you can use pip:

pip install oaas-sdk2-py

Or, if you are using uv:

# For adding/installing packages with uv, the command is 'uv pip install'
uv add oaas-sdk2-py

Features

  • Simplified API: Easy-to-use decorators and type-safe method definitions
  • Type Safety: Full Pydantic model support with automatic validation
  • Async/Sync Support: Built with async/await for non-blocking operations
  • Data Persistence: Object data is persisted and can be retrieved
  • Remote Procedure Calls (RPC): Invoke methods on objects remotely
  • Mocking Framework: Includes a mocking utility for testing your OaaS applications
  • Rust-Powered Core: High-performance core components written in Rust for speed and efficiency
  • Accessor Methods: Explicit @oaas.getter/@oaas.setter for typed persisted fields (not exported as standalone functions)

Quick Start

Basic Service Definition

from oaas_sdk2_py import oaas, OaasObject, OaasConfig
from pydantic import BaseModel

# Configure OaaS
config = OaasConfig(async_mode=True, mock_mode=False)
oaas.configure(config)

# Define request/response models
class GreetRequest(BaseModel):
    name: str

class GreetResponse(BaseModel):
    message: str

# Define your service
@oaas.service("Greeter", package="example")
class Greeter(OaasObject):
    counter: int = 0
    
    @oaas.method()
    async def greet(self, req: GreetRequest) -> GreetResponse:
        return GreetResponse(message=f"Hello, {req.name}!")
    
    @oaas.method()
    async def count_greetings(self) -> int:
        self.counter += 1
        return self.counter
    
    @oaas.method()
    async def is_popular(self, threshold: int = 10) -> bool:
        return self.counter > threshold

    # Accessor methods (not exported as RPC functions)
    @oaas.getter()
    async def get_counter(self) -> int:
        ...

    @oaas.setter()
    async def set_counter(self, value: int) -> int:
        ...

# Usage
async def main():
    # Create and use locally
    greeter = Greeter.create(local=True)
    
    # Test with different types
    response = await greeter.greet(GreetRequest(name="World"))
    count = await greeter.count_greetings()  # Returns int
    popular = await greeter.is_popular(5)     # Returns bool
    current_counter = await greeter.get_counter()  # Accessor getter
    _ = await greeter.set_counter(42)             # Accessor setter
    
    print(f"{response.message} (Count: {count}, Popular: {popular})")

Server & Agent Management

# Start gRPC server (for external access)
oaas.start_server(port=8080)

# Start agent (for background processing)
agent_id = await oaas.start_agent(Greeter, obj_id=123)

# Check status
print(f"Server running: {oaas.is_server_running()}")
print(f"Agents: {oaas.list_agents()}")

# Cleanup
await oaas.stop_agent(agent_id)
oaas.stop_server()

API Overview

Core Components

  • @oaas.service: Decorator to define OaaS services
  • @oaas.method: Decorator to expose methods as RPC endpoints
  • @oaas.getter/@oaas.setter: Accessor decorators for persisted fields (not exported as RPC functions)
  • OaasObject: Base class for all OaaS objects with persistence
  • OaasConfig: Configuration for OaaS runtime

Supported Types

The SDK natively supports these Python types:

  • Primitives: int, float, bool, str
  • Collections: list, dict
  • Binary: bytes
  • Models: Pydantic BaseModel classes

Examples

System Monitoring Service

import psutil
from oaas_sdk2_py import oaas, OaasObject

@oaas.service("ComputeDevice", package="monitoring")
class ComputeDevice(OaasObject):
    metrics: dict = {}

    @oaas.method()
    async def get_cpu_usage(self) -> float:
        """Get current CPU usage as a percentage."""
        return psutil.cpu_percent(interval=0.1)

    @oaas.method()
    async def get_process_count(self) -> int:
        """Get number of running processes."""
        return len(psutil.pids())

    @oaas.method()
    async def is_healthy(self, cpu_threshold: float = 80.0) -> bool:
        """Check if system is healthy."""
        cpu_usage = await self.get_cpu_usage()
        return cpu_usage < cpu_threshold

    @oaas.method()
    async def monitor_continuously(self, duration: int) -> dict:
        """Monitor for specified duration and return metrics."""
        samples = []
        for _ in range(duration):
            cpu = psutil.cpu_percent(interval=1.0)
            samples.append(cpu)
        
        return {
            "avg_cpu": sum(samples) / len(samples),
            "samples": len(samples),
            "duration": duration
        }

# Usage
device = ComputeDevice.create(local=True)
cpu_usage = await device.get_cpu_usage()      # Returns float
process_count = await device.get_process_count()  # Returns int
is_healthy = await device.is_healthy(75.0)    # Returns bool
metrics = await device.monitor_continuously(5)  # Returns dict

Counter Service with State

from oaas_sdk2_py import oaas, OaasObject

@oaas.service("Counter", package="example")
class Counter(OaasObject):
    count: int = 0
    history: list = []

    @oaas.method()
    async def increment(self, amount: int = 1) -> int:
        """Increment counter by amount."""
        self.count += amount
        self.history.append(f"Added {amount}")
        return self.count

    @oaas.method()
    async def reset(self) -> bool:
        """Reset counter to zero."""
        self.count = 0
        self.history.clear()
        return True

    # Accessors for the state fields
    @oaas.getter()
    async def get_count(self) -> int:
        ...

    @oaas.setter()
    async def set_count(self, value: int) -> int:
        ...

    @oaas.getter("history")
    async def get_history(self) -> list:
        ...

# Usage
counter = Counter.create(local=True)
value = await counter.increment(5)     # Returns int: 5
current = await counter.get_count()    # Accessor getter: 5
history = await counter.get_history()  # Accessor getter: ["Added 5"]
reset = await counter.reset()          # Returns bool: True
await counter.set_count(5)             # Accessor setter: set to 5
value = await counter.get_count()      # Accessor getter: 5

Why use accessors over methods?

Primary goal: avoid redundant RPC for simple state access. A method does RPC → run code → access data → RPC reply; an accessor reads/writes the persisted data directly.

Accessors (@oaas.getter/@oaas.setter) are the preferred way to expose simple reads/writes of persisted fields:

  • Smaller external surface: accessors are not exported as standalone RPC functions, reducing accidental public APIs.
  • Type-safe binding: validated against the field’s annotation at registration; name-based inference cuts boilerplate.
  • Clear semantics: getters don’t have side effects; setters just write. Easy to reason about and review.
  • Projection support: getters can return a projected sub-value from structured fields (e.g., dict/model paths).

Before (method):

@oaas.method()
async def get_count(self) -> int:
    return self.count

After (accessor):

@oaas.getter("count")
async def get_count(self) -> int:
    ...  # body not required; semantics = read persisted field

Testing with Mock Mode

import pytest
from oaas_sdk2_py import oaas, OaasConfig

# Configure for testing
@pytest.fixture
def setup_mock():
    config = OaasConfig(mock_mode=True, async_mode=True)
    oaas.configure(config)

@pytest.mark.asyncio
async def test_counter_service(setup_mock):
    counter = Counter.create(local=True)
    
    # Test increment
    result = await counter.increment(10)
    assert result == 10
    assert isinstance(result, int)
    
    # Test history
    history = await counter.get_history()
    assert history == ["Added 10"]
    assert isinstance(history, list)
    
    # Test reset
    reset_result = await counter.reset()
    assert reset_result is True
    assert isinstance(reset_result, bool)

Running as Server/Agent

Create a main module to run your service:

# main.py
import asyncio
import sys
from oaas_sdk2_py import oaas, OaasConfig

async def run_server():
    """Run gRPC server for external access."""
    config = OaasConfig(async_mode=True, mock_mode=False)
    oaas.configure(config)
    
    oaas.start_server(port=8080)
    print("🚀 Server running on port 8080")
    
    try:
        while True:
            await asyncio.sleep(1)
    except KeyboardInterrupt:
        print("🛑 Shutting down...")
    finally:
        oaas.stop_server()

async def run_agent():
    """Run agent for background processing."""
    config = OaasConfig(async_mode=True, mock_mode=False)
    oaas.configure(config)
    
    # Start both server and agent
    oaas.start_server(port=8080)
    agent_id = await oaas.start_agent(Counter, obj_id=1)
    print(f"🤖 Agent started: {agent_id}")
    
    try:
        while True:
            await asyncio.sleep(5)
            print(f"📊 Server: {oaas.is_server_running()}, Agents: {len(oaas.list_agents())}")
    except KeyboardInterrupt:
        print("🛑 Shutting down...")
    finally:
        await oaas.stop_all_agents()
        oaas.stop_server()

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "agent":
        asyncio.run(run_agent())
    else:
        asyncio.run(run_server())

Run with:

# Server only
python main.py

# Server + Agent
python main.py agent

Run on OaaS

Prerequisites

  • cargo (install via rust)
  • oprc-cli cargo install --git https://github.com/pawissanutt/oaas-rs.git oprc-cli
  • OaaS Platform (Oparaca)
    • Kubernetes Cluster (e.g., k3d with Docker runtime)

Build the project

You don't need to follow this guide unless you want to build the Python package on your own.

Prerequisites

  • Python
  • cargo (install via rust)
  • uv (python package manager)

Build

uv sync
uv build

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oaas_sdk2_py-0.3.1.tar.gz (101.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

oaas_sdk2_py-0.3.1-py3-none-any.whl (114.4 kB view details)

Uploaded Python 3

File details

Details for the file oaas_sdk2_py-0.3.1.tar.gz.

File metadata

  • Download URL: oaas_sdk2_py-0.3.1.tar.gz
  • Upload date:
  • Size: 101.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.6

File hashes

Hashes for oaas_sdk2_py-0.3.1.tar.gz
Algorithm Hash digest
SHA256 8485d9532a0402a3e89599f40a1477dfdf529b1de3cc722601d9e245dd47ddf3
MD5 7c5059c21b437f56dd5e9de139940ea5
BLAKE2b-256 2c48d840e444d65c48b840fa12bd82cfc4aed0420ae47b4b2928f11a0c659998

See more details on using hashes here.

File details

Details for the file oaas_sdk2_py-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: oaas_sdk2_py-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 114.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.6

File hashes

Hashes for oaas_sdk2_py-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4241d3b9ef4a6738864efb6625b3a19411615bdb6935b031388c1b95a9fd66de
MD5 16cf81418b375f93ab3339f2babf5760
BLAKE2b-256 6d92776840ff35d414c4655324317d1ad51e2e0c8a50b1d7ae6c2ecec44c584c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page