Quickstart

Install the latest stable version into your virtual environment:

pip install sosw

sosw requires Python 3.12 – 3.14 and depends only on boto3 (which the AWS Lambda runtimes already provide).

A minimal Processor Lambda

Create app.py with the following structure. Your business logic lives in a subclass of Processor; the module-level lambda_handler is generated by sosw.app.get_lambda_handler().

Note

AWS STS is used here just as an example of automatic initialization of boto3 clients.

from sosw.app import LambdaGlobals, get_lambda_handler, Processor as SoswProcessor
from sosw.components.benchmark import benchmark


class Processor(SoswProcessor):

    DEFAULT_CONFIG = {
        'init_clients': ['sts'],    # Automatically initialize `self.sts_client`.
    }

    sts_client = None


    def __call__(self, event, **kwargs):
        super().__call__(event)

        data = self.get_self_identity()
        self.result['accounts_identified'] += 1

        return {'account': data['Account'], **self.get_stats()}


    @benchmark   # Collect execution time in `self.stats`.
    def get_self_identity(self):
        return self.sts_client.get_caller_identity()


global_vars = LambdaGlobals()
lambda_handler = get_lambda_handler(Processor, global_vars)

What happens here:

  • On the first (cold) invocation the handler constructs the Processor: it assembles the configuration (DEFAULT_CONFIG, then optional overrides from DynamoDB / SSM, then custom_config — see Configuration) and initializes the clients listed in init_clients.

  • On warm invocations the very same Processor instance is reused from global_vars — no re-initialization, no extra latency. Read more in Warm start.

  • self.result is your per-invocation accumulator, self.stats collects per-container counters that the handler logs after every call.

If you do not want the Processor to look for external configuration in DynamoDB at all, disable the lookup explicitly:

class Processor(SoswProcessor):

    DEFAULT_CONFIG = {
        'disable_ddb_config': True,     # Skip the DynamoDB / SSM config lookup.
        'init_clients':       ['sts'],
    }

Deploy with AWS SAM

The fastest way to ship this function is AWS SAM. A minimal template.yaml:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: My first sosw-based Lambda.

Resources:

  MyFirstSoswFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: my-first-sosw-function
      CodeUri: src/
      Handler: app.lambda_handler
      Runtime: python3.14
      Timeout: 30
      MemorySize: 256

Put app.py into src/ together with a requirements.txt containing the single line sosw, then:

sam build && sam deploy --guided
aws lambda invoke --function-name my-first-sosw-function --payload '{}' /dev/stdout

For a complete walkthrough (project layout, configuration table, unit tests, cleanup) follow the Your first sosw Lambda tutorial.

Test it

sosw Processors are designed to be unit-tested without any network access. Mock the config lookup and boto3, then call the Processor directly:

import os
import unittest

from unittest.mock import MagicMock, patch

os.environ['STAGE'] = 'test'
os.environ['autotest'] = 'True'

from app import Processor


class ProcessorTestCase(unittest.TestCase):

    @patch('boto3.client')
    def setUp(self, mock_boto_client):
        with patch.object(Processor, 'get_config', return_value={}):
            self.processor = Processor()

        self.processor.sts_client = MagicMock()
        self.processor.sts_client.get_caller_identity.return_value = {'Account': '000000000000'}


    def test_call(self):
        result = self.processor({})

        self.assertEqual(result['account'], '000000000000')

Setting STAGE=test before importing makes the Processor derive test=True (see sosw.app._derive_test_flag()); patching get_config keeps the test free of DynamoDB calls.

Next steps