Metadata-Version: 2.4
Name: local-lambda-playground
Version: 0.1.1
Summary: Local development gateway for AWS SAM Lambda functions with FastAPI + Swagger UI
License: MIT
Keywords: aws,development,fastapi,lambda,local,sam
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.12
Requires-Dist: fastapi>=0.115.0
Requires-Dist: pydantic>=2.10.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.0.0
Requires-Dist: structlog>=24.4.0
Requires-Dist: typer>=0.12.0
Requires-Dist: uvicorn[standard]>=0.32.0
Requires-Dist: watchdog>=5.0.0
Provides-Extra: dev
Requires-Dist: httpx>=0.27.0; extra == 'dev'
Requires-Dist: mypy>=1.12.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.7.0; extra == 'dev'
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
Description-Content-Type: text/markdown

# local-lambda-playground

> **Local development gateway for AWS SAM Lambda functions.**  
> Reads your `template.yml`, discovers every HTTP route automatically, starts a FastAPI server with Swagger UI, assumes IAM roles, and hot-reloads on every file change — all from a single CLI command.

```bash
llp start --all
```

```
╭──────────────────────────────────────────────────────────────────────────────╮
│                          Registered Routes                                   │
├────────────┬────────┬───────────────────────────────┬────────────────────────┤
│ Group      │ Method │ Path                          │ IAM Role               │
├────────────┼────────┼───────────────────────────────┼────────────────────────┤
│ buyer-api  │ POST   │ /api/v1/auth/signup           │ ambient                │
│ buyer-api  │ POST   │ /api/v1/auth/login            │ ambient                │
│ buyer-api  │ POST   │ /api/v1/auth/refresh          │ ambient                │
│ buyer-api  │ POST   │ /api/v1/otp/send              │ ambient                │
│ seller-api │ POST   │ /api/v1/seller/register       │ agarmart-seller-role   │
│ admin-api  │ POST   │ /api/v1/admin/auth/login      │ agarmart-admin-role    │
╰────────────┴────────┴───────────────────────────────┴────────────────────────╯

Swagger UI:   http://localhost:8000/docs
ReDoc:        http://localhost:8000/redoc
```

---

## Table of Contents

1. [What Is local-lambda-playground?](#what-is-local-lambda-playground)
2. [Why Use It?](#why-use-it)
3. [How It Compares to sam local start-api](#how-it-compares-to-sam-local-start-api)
4. [Requirements](#requirements)
5. [Installation](#installation)
6. [Quick Start](#quick-start)
7. [CLI Commands](#cli-commands)
   - [start](#start--launch-the-gateway)
   - [list](#list--print-all-function-ids)
   - [routes](#routes--print-all-http-routes)
   - [invoke](#invoke--call-a-function-directly)
   - [install](#install--set-up-per-function-venvs)
8. [Filtering & Targeting Functions](#filtering--targeting-functions)
9. [IAM Role Support](#iam-role-support)
10. [Auth Mock](#auth-mock)
11. [Environment Variables & Configuration](#environment-variables--configuration)
12. [Hot Reload](#hot-reload)
13. [Swagger UI & API Documentation](#swagger-ui--api-documentation)
14. [Dependency Isolation](#dependency-isolation)
15. [How It Works Internally](#how-it-works-internally)
16. [Project Structure](#project-structure)
17. [Testing](#testing)
18. [Troubleshooting](#troubleshooting)

---

## What Is local-lambda-playground?

`llp` is a developer tool that makes it fast and painless to run **AWS SAM Lambda functions** on your local machine — without Docker, without SAM CLI, and without slow cold starts.

It reads your `template.yml`, discovers all `AWS::Serverless::Function` resources that have API Gateway events, and dynamically registers them as routes in a live **FastAPI** web server. Every route is immediately browsable in **Swagger UI**, hot-reloads when you change any source file, and runs with the exact same IAM permissions your function would have in production.

---

## Why Use It?

### The problem with `sam local start-api`

- **Slow** — spins up a Docker container for every request (3–10 seconds cold start)
- **Opaque** — no Swagger UI, no structured request/response logging
- **Brittle** — Docker dependency, port conflicts, environment variable setup is tedious
- **No IAM** — cannot assume real IAM roles locally

### What local-lambda-playground gives you

| Benefit | Detail |
|---|---|
| ⚡ **Instant startup** | Python import, no Docker — functions start in milliseconds |
| 📖 **Auto Swagger UI** | Every route documented at `/docs` the moment the server starts |
| 🔄 **Hot reload** | Edit any handler `.py` or `template.yml` — server reloads automatically |
| 🔑 **Real IAM roles** | Calls STS `AssumeRole` using the `Role:` ARN from your template |
| 🧪 **Auth mock** | Bypass JWT auth in local testing with `--mock-user-id` |
| 🌿 **Smart filtering** | Run only buyer-api, or only `Buyer*` functions, or everything except `Admin*` |
| 📦 **Zero extra config** | Points at your existing `template.yml` — nothing else needed |
| 🗂 **Grouped Swagger** | Routes are tagged by folder group (`buyer-api`, `seller-api`, `admin-api`) |
| 🔒 **Credential safety** | IAM credentials injected per-invocation and cleaned up immediately after |

---

## How It Compares to sam local start-api

| Capability | llp | sam local start-api |
|---|---|---|
| Startup speed | < 1 second | 5–30 seconds (Docker pull + container) |
| Requires Docker | No | Yes |
| Swagger UI | Yes — auto-generated | No |
| Route grouping in docs | Yes — by folder | No |
| Hot reload | Yes — uvicorn + watchdog | No (manual restart) |
| IAM role assumption | Yes — STS AssumeRole | No |
| Auth mock | Yes — `--mock-user-id` | No |
| Per-function env vars | Yes — `env.json` per logical ID | Yes — `env.json` |
| CF intrinsic handling | Graceful (strips/resolves) | Full CloudFormation engine |
| Parity with Lambda runtime | High (same Python interpreter) | Exact (Docker image match) |
| Offline usage | Yes | Yes (with container cached) |

> **Rule of thumb:** Use `llp` during active development for fast feedback loops.  
> Use `sam local` for final integration testing before deployment.

---

## Requirements

- Python **3.10+**
- AWS credentials configured (only needed for IAM role assumption — optional)

---

## Installation

### From PyPI

```bash
pip install local-lambda-playground
```

### From source (development mode)

```bash
cd lambda-local/
pip install -e ".[dev]"
```

### Verify

```bash
llp --help
```

---

## Quick Start

```bash
# 1. Navigate to your SAM project
cd AgarMart-serverless-api/

# 2. Create a local env config (copy from example)
cp .env.example .env

# Fill in local values:
# MONGODB_URI=mongodb://localhost:27017
# REDIS_URL=redis://localhost:6379
# JWT_SECRET=my-dev-secret

# 3. Start ALL Lambda functions
llp start --all

# 4. Open Swagger UI
open http://localhost:8000/docs
```

That's it. Every route from your `template.yml` is live and documented.

---

## CLI Commands

### `start` — Launch the gateway

```bash
llp start [OPTIONS]
```

| Option | Short | Default | Description |
|---|---|---|---|
| `--all` | `-a` | `false` | Run ALL functions (ignores other filters) |
| `--function NAME` | `-f` | — | Include only this function (repeatable) |
| `--group NAME` | `-g` | — | Include functions in this folder group (repeatable) |
| `--match PATTERN` | `-m` | — | Glob pattern for logical IDs e.g. `Buyer*` (repeatable) |
| `--exclude PATTERN` | `-e` | — | Glob pattern to exclude e.g. `Admin*` (repeatable) |
| `--template PATH` | `-t` | `template.yml` | Path to SAM template |
| `--host HOST` | — | `127.0.0.1` | Bind address |
| `--port PORT` | — | `8000` | Bind port |
| `--reload / --no-reload` | — | `--reload` | Enable / disable hot reload |
| `--env-file PATH` | — | auto | Path to `.env` file |
| `--env-json PATH` | — | auto | Path to `env.json` |
| `--mock-user-id ID` | — | — | Inject a fake user into `requestContext.authorizer` |
| `--mock-role ROLE` | — | `buyer` | Role for the mock user |
| `--mock-email EMAIL` | — | `dev@localhost` | Email for the mock user |
| `--role-arn ARN` | — | — | Assume this IAM role for ALL functions |
| `--no-role` | — | `false` | Skip role assumption; use ambient AWS credentials |

---

### `list` — Print all function IDs

```bash
llp list [--template PATH]
```

Lists every `AWS::Serverless::Function` logical ID in the template (alphabetically sorted).

```
AdminAuthFunction
AdminCategoriesFunction
BuyerAuthFunction
BuyerOtpFunction
SellerAuthFunction
SellerProductsFunction
...
```

---

### `routes` — Print all HTTP routes

```bash
llp routes [--template PATH] [--group NAME]
```

```
 Method  Path                              Group       Handler
 POST    /api/v1/auth/signup               buyer-api   auth/handler.lambda_handler
 POST    /api/v1/auth/login                buyer-api   auth/handler.lambda_handler
 GET     /api/v1/buyer/profile             buyer-api   profile/handler.lambda_handler
 POST    /api/v1/seller/register           seller-api  auth/handler.lambda_handler
 GET     /api/v1/seller/products           seller-api  products/handler.lambda_handler
 POST    /api/v1/admin/auth/login          admin-api   auth/handler.lambda_handler
```

Filter by group:

```bash
llp routes --group seller-api
```

---

### `invoke` — Call a function directly

```bash
llp invoke FUNCTION_ID [OPTIONS]
```

| Option | Description |
|---|---|
| `--event PATH` | JSON file with the full Lambda event payload |
| `--template PATH` | Path to SAM template |
| `--env-json PATH` | Path to env.json |
| `--role-arn ARN` | Assume this IAM role before invoking |
| `--no-role` | Skip role assumption |

**Basic invoke (event auto-built from route metadata):**

```bash
llp invoke BuyerAuthFunction
```

**Invoke with a custom event file:**

```bash
llp invoke BuyerAuthFunction --event events/login.json
```

Example `events/login.json`:

```json
{
  "httpMethod": "POST",
  "path": "/api/v1/auth/login",
  "headers": { "Content-Type": "application/json" },
  "body": "{\"email\": \"user@example.com\", \"password\": \"secret123\"}",
  "queryStringParameters": null,
  "pathParameters": null,
  "requestContext": {
    "stage": "local",
    "identity": { "sourceIp": "127.0.0.1" },
    "authorizer": {}
  }
}
```

Output:

```json
{
  "statusCode": 200,
  "headers": { "Content-Type": "application/json" },
  "body": {
    "success": true,
    "data": {
      "access_token": "eyJ...",
      "refresh_token": "eyJ...",
      "token_type": "Bearer"
    }
  }
}
```

---

### `install` — Set up per-function venvs

```bash
llp install [OPTIONS]
```

| Option | Description |
|---|---|
| `--group NAME` | Install only this group (repeatable) |
| `--force` | Recreate venvs from scratch |
| `--template PATH` | Path to SAM template |

Creates an isolated virtual environment for each function group:

```
.lambda-local/
├── buyer-api/    ← buyer-api/requirements.txt installed here
├── seller-api/
└── admin-api/
```

```bash
llp install           # all groups
llp install -g buyer-api
llp install --force   # rebuild from scratch
```

> Add `.lambda-local/` to your `.gitignore`.

---

## Filtering & Targeting Functions

### Run everything

```bash
llp start --all
```

### Single function

```bash
llp start -f BuyerAuthFunction
```

### Multiple specific functions

```bash
llp start -f BuyerAuthFunction -f BuyerOtpFunction
```

### By folder group

The group comes from the first segment of `CodeUri`:

```yaml
BuyerAuthFunction:
  Properties:
    CodeUri: buyer-api/auth/   # group = "buyer-api"
```

```bash
llp start --group buyer-api
llp start --group buyer-api --group seller-api
```

### Wildcard pattern

```bash
llp start --match "Buyer*"      # everything starting with Buyer
llp start --match "*Auth*"      # everything containing Auth
llp start --match "Admin*"      # all admin functions
```

### Exclude

```bash
llp start --all --exclude "Admin*"
llp start --all --exclude "*Job*"
```

### Combine

```bash
# buyer-api but not the social auth endpoint
llp start --group buyer-api --exclude "BuyerSocialAuthFunction"
```

---

## IAM Role Support

In production, each Lambda function runs under an IAM execution role:

```yaml
BuyerAuthFunction:
  Type: AWS::Serverless::Function
  Properties:
    Role: arn:aws:iam::123456789012:role/agarmart-buyer-role
    # or with CloudFormation Sub:
    Role: !Sub 'arn:aws:iam::${AWS::AccountId}:role/agarmart-${StageName}'
```

`llp` replicates this by calling **STS AssumeRole** before each function invocation and injecting the temporary credentials as environment variables — so your function can call S3, SQS, DynamoDB, or any AWS service with real permissions.

### How credentials are resolved

```
Request arrives
      │
      ▼
  Read role_arn from FunctionRoute
  (extracted from template.yml Role: property)
      │
      ▼
  --role-arn override? → use that instead
      │
      ▼
  Resolve CloudFormation Sub variables:
    ${AWS::AccountId}  →  real account ID via STS GetCallerIdentity
    ${AWS::Region}     →  boto3 default region
    ${AWS::Partition}  →  aws
      │
      ▼
  Check credential cache (cached for 1 hour)
  Cache hit → use cached credentials
  Cache miss → STS AssumeRole(...)
      │
      ▼
  Inject into environment:
    AWS_ACCESS_KEY_ID     = <temp>
    AWS_SECRET_ACCESS_KEY = <temp>
    AWS_SESSION_TOKEN     = <temp>
      │
      ▼
  Call lambda_handler(event, context)
      │
      ▼
  Restore original credentials
```

### CLI options

```bash
# Per-function roles from template.yml (default behaviour)
llp start --all

# Override: assume one role for ALL functions
llp start --all \
  --role-arn arn:aws:iam::123456789012:role/my-dev-role

# Skip role assumption entirely
llp start --all --no-role

# Set via environment variable (persists across reloads)
export LAMBDA_LOCAL_ROLE_ARN=arn:aws:iam::123456789012:role/my-dev-role
llp start --all

# invoke command also supports role options
llp invoke BuyerAuthFunction \
  --role-arn arn:aws:iam::123456789012:role/agarmart-dev
```

### CloudFormation Sub variables resolved

| Variable | Resolved to |
|---|---|
| `${AWS::AccountId}` | Real account ID via `sts:GetCallerIdentity` |
| `${AWS::Region}` | boto3 default region or `AWS_REGION` env var |
| `${AWS::Partition}` | `aws` |
| `${StageName}` *(custom)* | Stripped (empty string — override with `--role-arn`) |

### Required developer permissions

To assume a role, your IAM user/role needs:

```json
{
  "Effect": "Allow",
  "Action": "sts:AssumeRole",
  "Resource": "arn:aws:iam::123456789012:role/agarmart-*"
}
```

And the target role must list your IAM principal in its **Trust Policy**.

### Graceful fallback

If `AssumeRole` fails (no permission, unresolvable ARN, no AWS credentials), llp logs a warning and continues with **ambient credentials**. The server never crashes on a role assumption failure. Use `--no-role` to suppress the warning.

---

## Auth Mock

Most Lambda functions check `event["requestContext"]["authorizer"]` for user identity (injected by the Lambda Authorizer in production). In local dev you don't want to generate real JWTs.

Use `--mock-user-id` to inject a fake user into every request:

```bash
llp start --all \
  --mock-user-id user_abc123 \
  --mock-role buyer \
  --mock-email test@example.com
```

Every request will contain:

```json
{
  "requestContext": {
    "authorizer": {
      "user_id": "user_abc123",
      "role":    "buyer",
      "email":   "test@example.com"
    }
  }
}
```

Your handler's `get_current_user(event)` succeeds without any JWT validation.

**Test different roles simultaneously:**

```bash
# Terminal 1 — buyer on port 8001
llp start --group buyer-api --port 8001 \
  --mock-user-id buyer_001 --mock-role buyer

# Terminal 2 — admin on port 8002
llp start --group admin-api --port 8002 \
  --mock-user-id admin_001 --mock-role admin
```

---

## Environment Variables & Configuration

### Auto-detection

`llp` scans the template's directory automatically:

```
AgarMart-serverless-api/
├── template.yml
├── .env.local       ← checked first
├── .env             ← checked second
└── env.json         ← checked third
```

### Explicit paths

```bash
llp start --env-file .env.staging --env-json env.staging.json
```

### `.env` format

```bash
MONGODB_URI=mongodb://localhost:27017
MONGODB_DB_NAME=agarmart
REDIS_URL=redis://localhost:6379
JWT_SECRET=dev-only-secret-256-bits
JWT_ACCESS_EXPIRE_MINUTES=15
JWT_REFRESH_EXPIRE_DAYS=30
AWS_S3_BUCKET=agarmart-dev-media
AWS_REGION=ap-south-1
CDN_BASE_URL=https://cdn.innobuz.com/app
BREVO_API_KEY=xkeysib-...
FAST2SMS_API_KEY=...
GOOGLE_CLIENT_ID=...
SQS_EMAIL_QUEUE_URL=https://sqs.ap-south-1.amazonaws.com/123456789012/agarmart-email-dev
SQS_SMS_QUEUE_URL=https://sqs.ap-south-1.amazonaws.com/123456789012/agarmart-sms-dev
SQS_PUSH_QUEUE_URL=https://sqs.ap-south-1.amazonaws.com/123456789012/agarmart-push-dev
```

### `env.json` — SAM Parameters format

```json
{
  "Parameters": {
    "MONGODB_URI": "mongodb://localhost:27017",
    "JWT_SECRET": "dev-secret",
    "REDIS_URL": "redis://localhost:6379"
  }
}
```

### `env.json` — Per-function format

Different values per Lambda function:

```json
{
  "BuyerAuthFunction": {
    "MONGODB_URI": "mongodb://localhost:27017/buyer_test"
  },
  "AdminAuthFunction": {
    "MONGODB_URI": "mongodb://localhost:27017/admin_test",
    "JWT_SECRET": "admin-specific-secret"
  }
}
```

### CloudFormation intrinsic handling

`template.yml` often contains SSM references that can't be resolved locally:

```yaml
JWT_SECRET: !Sub '{{resolve:ssm:/agarmart/prod/jwt_secret}}'
```

llp **strips these automatically** and replaces them with empty strings — the server starts without errors. Override with real local values in your `.env`.

### Priority order (highest wins)

```
1. Actual process environment (export MY_VAR=x before starting)
2. --env-file / .env.local / .env
3. --env-json / env.json (Parameters key)
4. Per-function env.json block
```

---

## Hot Reload

Hot reload is **on by default**. The server watches:

| File change | Effect |
|---|---|
| Any `**/*.py` handler file | Module reloaded; next request uses new code |
| `template.yml` | Routes re-discovered; Swagger UI updates |
| `env.json` / `.env` | Environment re-loaded |
| `requirements.txt` | No auto-install; run `llp install` then restart |

```bash
# Reload is on by default
llp start --all

# Disable (useful in CI)
llp start --all --no-reload
```

---

## Swagger UI & API Documentation

| URL | Interface |
|---|---|
| `http://localhost:8000/docs` | Swagger UI — interactive API explorer |
| `http://localhost:8000/redoc` | ReDoc — clean reference docs |
| `http://localhost:8000/openapi.json` | Raw OpenAPI 3.0 schema |

Routes are tagged by **group** (folder prefix) so Swagger UI shows:

```
► buyer-api
    POST  /api/v1/auth/signup
    POST  /api/v1/auth/login
    GET   /api/v1/buyer/profile

► seller-api
    POST  /api/v1/seller/register
    GET   /api/v1/seller/products

► admin-api
    POST  /api/v1/admin/auth/login
    GET   /api/v1/admin/dashboard/stats
```

If you started with `--mock-user-id`, protected routes work directly in the Swagger UI without needing a Bearer token.

---

## How It Works Internally

```
llp start --all
        │
        ▼
┌─────────────────────────────────────────────────────────┐
│  CLI  (cli.py)                                          │
│  Parse args → preview table → set LAMBDA_LOCAL_* env   │
│  → start uvicorn with factory=True reload=True          │
└────────────────────┬────────────────────────────────────┘
                     │ on every (re)start
                     ▼
┌─────────────────────────────────────────────────────────┐
│  Gateway  (gateway.py)  create_app()                    │
│  Read LAMBDA_LOCAL_* env vars                           │
│  → discover_routes()                                    │
│  → detect Lambda layer paths (shared/python/)           │
│  → create FastAPI app with Swagger tags                 │
│  → register one route per HTTP event                    │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│  Discovery  (discovery.py)                              │
│  CF-aware YAML loader (handles !Ref, !Sub, !GetAtt)     │
│  Extract: logical_id handler code_uri path method       │
│           group role_arn                                │
│  Apply --function / --group / --match / --exclude       │
└─────────────────────────────────────────────────────────┘

HTTP request arrives
        │
        ▼
┌─────────────────────────────────────────────────────────┐
│  Route handler closure  (gateway.py)                    │
│  → Invoker.invoke(route, request)                       │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│  Invoker  (invoker.py)                                  │
│  Build API Gateway event dict from FastAPI request      │
│  Submit to ThreadPoolExecutor                           │
│  (asyncio.run() inside handlers is safe in threads)     │
└────────────────────┬────────────────────────────────────┘
                     │ in thread
                     ▼
┌─────────────────────────────────────────────────────────┐
│  _invoke_in_thread                                      │
│                                                         │
│  1. Inject per-function env vars (env.json)             │
│                                                         │
│  2. IAM credential injection  (iam.py)                  │
│     • Resolve ${AWS::AccountId} etc. in role ARN        │
│     • STS AssumeRole (cached 1 hr, refresh 5 min early) │
│     • Set AWS_ACCESS_KEY_ID / SECRET / SESSION_TOKEN    │
│                                                         │
│  3. sys.path: prepend code_uri + layer paths            │
│                                                         │
│  4. importlib.util.spec_from_file_location              │
│     unique module key per logical_id                    │
│     (no naming conflicts across handlers)               │
│                                                         │
│  5. handler_fn(event, context)                          │
│                                                         │
│  6. Restore IAM credentials + env vars                  │
└─────────────────────────────────────────────────────────┘
```

---

## Project Structure

```
lambda-local/
├── pyproject.toml              ← packaging, deps, tool config (Ruff, MyPy, pytest)
├── README.md
│
├── lambda_local/
│   ├── __init__.py
│   ├── cli.py                  ← Typer CLI (start, list, routes, invoke, install)
│   ├── config.py               ← .env / env.json loading + CF intrinsic stripping
│   ├── discovery.py            ← SAM template parser + FunctionRoute builder
│   ├── gateway.py              ← FastAPI app factory (create_app)
│   ├── iam.py                  ← STS AssumeRole + credential caching + injection
│   ├── installer.py            ← Per-group venv creator (llp install)
│   ├── invoker.py              ← Lambda handler runner (thread pool executor)
│   ├── models.py               ← Pydantic models: FunctionRoute, InvocationResult
│   └── watcher.py              ← watchdog FileWatcher (debounced 500ms)
│
└── tests/
    ├── conftest.py             ← Fixtures: templates, handler files
    ├── test_discovery.py       ← Route discovery + filtering
    ├── test_invoker.py         ← Event building + handler invocation
    ├── test_gateway.py         ← FastAPI app + HTTP integration tests
    └── test_cli.py             ← CLI command tests (Typer TestClient)
```

---

## Testing

```bash
pip install -e ".[dev]"

# Full suite with coverage
pytest

# Coverage HTML report
pytest --cov=lambda_local --cov-report=html
open htmlcov/index.html

# Specific file
pytest tests/test_discovery.py -v

# Single test
pytest tests/test_invoker.py::TestBuildEvent::test_mock_user_injected -v
```

Target coverage: **> 90%** (enforced by pytest config).

---

## Troubleshooting

### `requires a different Python: 3.x not in '>=3.10'`

Switch to Python 3.10+:

```bash
pyenv install 3.12.0 && pyenv local 3.12.0
pip install local-lambda-playground
```

---

### `Template not found: template.yml`

Run from the directory containing your template, or pass `--template`:

```bash
llp start --all --template /path/to/AgarMart-serverless-api/template.yml
```

---

### `Handler file not found`

Check that `CodeUri` is correct relative to `template.yml` and that `Handler` matches the actual filename:

```yaml
CodeUri: buyer-api/auth/     # directory must exist
Handler: handler.lambda_handler  # buyer-api/auth/handler.py must exist
```

---

### `ImportError: No module named 'agarmart'`

The shared layer isn't on the Python path. Declare it in `template.yml`:

```yaml
SharedLayer:
  Type: AWS::Serverless::LayerVersion
  Properties:
    ContentUri: shared/   # shared/python/ is added to sys.path automatically
```

Or install it directly:

```bash
pip install -r shared/requirements.txt
```

---

### `ModuleNotFoundError: No module named 'motor'`

Install the function's runtime deps:

```bash
pip install -r buyer-api/requirements.txt
pip install -r shared/requirements.txt
```

Or use the isolation approach:

```bash
llp install --group buyer-api
```

---

### `iam_assume_role_failed` warning

Causes and fixes:

| Cause | Fix |
|---|---|
| No AWS credentials | `aws configure` or set `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` |
| No `sts:AssumeRole` permission | Add the permission to your IAM user/role |
| Unresolvable ARN (`${StageName}` etc.) | Pass `--role-arn arn:aws:iam::ACCOUNT:role/NAME` explicitly |

To suppress the warning and use ambient credentials:

```bash
llp start --all --no-role
```

---

### Routes not updating after template edit

1. Confirm `--reload` is active (it is by default)
2. Check you're not in the `.lambda-local/` directory (excluded from watching)
3. Save the file again — some editors write atomically without triggering inotify
4. Hard restart: `Ctrl+C` → `llp start --all`

---

## License

MIT


[pypi]
  username = innux
  password = pypi-AgEIcHlwaS5vcmcCJDc5MjE1YTQ5LTEwNWItNDdhMS05MTI4LWFjNzY3MWVlYTFjMAACKlszLCJkMTI3ZGYyNS0wYjZhLTRiNjItODE4Yi1lODI5ODczOGRkMDUiXQAABiApc8NZEWfv0eaHXv1t1DeZv1Yix46pSmTkLPMq9a1CMA