Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add strict typing #16

Merged
merged 6 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# Smyth

<div align="center">
<img src="docs/assets/logo_white_small.svg" width="300" role="img">

[![docs](https://img.shields.io/badge/Docs-Smyth-f5c03b.svg?style=flat&logo=materialformkdocs)](https://mirumee.github.io/smyth/)
![pypi](https://img.shields.io/pypi/v/smyth?style=flat)
![licence](https://img.shields.io/pypi/l/smyth?style=flat)
![pypi downloads](https://img.shields.io/pypi/dm/smyth?style=flat)
![pyversion](https://img.shields.io/pypi/pyversions/smyth?style=flat)
</div>

Smyth is a versatile tool designed to enhance your AWS Lambda development experience. It is a pure Python tool that allows for easy customization and state persistence, making your Lambda development more efficient and developer-friendly.
Smyth is a tool designed to enhance your AWS Lambda development experience by mocking an AWS Lambda environment on your **local machine**. It is a pure Python tool that allows for easy customization and state persistence, making your Lambda development more efficient and developer-friendly.

## Features

Expand Down Expand Up @@ -43,6 +47,9 @@ handler_path = "my_project.handlers.saleor.handler.saleor_http_handler"
url_path = "/saleor/{path:path}"
```

> [!TIP]
> Check the [documentation](https://mirumee.github.io/smyth/user_guide/all_settings/) for more configuration options.

Run Smyth with:
```bash
python -m smyth
Expand Down Expand Up @@ -75,11 +82,6 @@ To utilize the VS Code debugger with the Smyth tool, you can set up your `launch

The combination of Uvicorn reload process and HTTP server process with what is being done with the Lambda processes is tricky. If a Lambda process is doing something and the HTTP server is killed in the wrong moment it's likely going to bork your terminal. This is not solved yet. It's best to use in a Docker container or have the ability to `kill -9 {PID of the Uvicorn reload process}` at hand.

## TODO

- [ ] Write tests
- [x] Publish on PyPi

## Name

This name blends "Smith" (as in a blacksmith, someone who works in a forge) with "Py" for Python, altering the spelling to "Smyth". Blacksmiths are craftsmen who work with metal in a forge, shaping it into desired forms. Similarly, "Smyth" suggests a tool that helps developers craft and shape their serverless projects with the precision and skill of a smith, but in the realm of Python programming. This name retains the essence of craftsmanship and transformation inherent in a forge while being associated with Python.
Expand Down
21 changes: 21 additions & 0 deletions docs/user_guide/all_settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,24 @@ Here's a list of all the settings, including those that are simpler but equally
### Strategy Generator

`strategy_generator_path` - `str` (default: `"smyth.runner.strategy.first_warm"`) Read more about [dispatch strategies here](concurrency.md/#dispatch-strategy).


## `pyproject.toml` example

```toml title='pyproject.toml' linenums="1"
[tool.smyth]
host = "0.0.0.0"
port = 8080
log_level = "INFO"
smyth_path_prefix = "/smyth"

[tool.smyth.handlers.lambda_handler]
handler_path = "nimara_search_algolia_backend.app.lambda_handler"
url_path = "{path:path}"
timeout = 300
event_data_function_path = "smyth.event.generate_api_gw_v2_event_data"
context_data_function_path = "smyth.context.generate_context_data"
log_level = "DEBUG"
concurrency = 3
strategy_generator_path = "smyth.runner.strategy.first_warm"
```
712 changes: 0 additions & 712 deletions poetry.lock

This file was deleted.

32 changes: 22 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ dynamic = ["version"]
description = "Smyth is a versatile tool designed to enhance your AWS Lambda development experience. It is a pure Python tool that allows for easy customization and state persistence, making your Lambda development more efficient and developer-friendly."
readme = "README.md"
requires-python = ">=3.10"
license = { file = "LICENSE.txt" }
license = "BSD-3-Clause"
license-files = ["LICENSE.txt"]
keywords = []
authors = [{ name = "Mirumee", email = "[email protected]" }]
classifiers = [
Expand Down Expand Up @@ -47,7 +48,7 @@ Source = "https://github.com/mirumee/smyth"
[project.optional-dependencies]
dev = ["ipdb"]
types = ["mypy>=1.0.0", "pytest", "types-toml", "pytest-asyncio"]
docs = ["mkdocs-material", "termynal"]
docs = ["mkdocs-material~=9.0", "termynal"]

[tool.hatch.version]
path = "src/smyth/__about__.py"
Expand Down Expand Up @@ -75,14 +76,6 @@ check = [
[tool.hatch.envs.types.scripts]
check = "mypy --install-types --non-interactive {args:src/smyth}"

[tool.mypy]
check_untyped_defs = true

[[tool.mypy.overrides]]
module = "setproctitle.*"
ignore_missing_imports = true


## Test environment

[tool.hatch.envs.hatch-test]
Expand Down Expand Up @@ -118,6 +111,25 @@ deploy = "mkdocs gh-deploy --force"

[tool.pytest.ini_options]

## Types configuration

[tool.mypy]
python_version = "3.10"
files = ["src/**/*.py"]
exclude = "tests/.*"
warn_redundant_casts = true
warn_unused_ignores = true
disallow_any_generics = true
check_untyped_defs = true
no_implicit_reexport = true
disallow_untyped_defs = true
strict = true
disable_error_code = ["import-untyped"]

[[tool.mypy.overrides]]
module = "setproctitle.*"
ignore_missing_imports = true

## Coverage configuration

[tool.coverage.run]
Expand Down
2 changes: 1 addition & 1 deletion src/smyth/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def run(
quiet: Annotated[
bool, typer.Option(help="Effectively the same as --log-level=ERROR")
] = False,
):
) -> None:
if host:
config.host = host
if port:
Expand Down
7 changes: 4 additions & 3 deletions src/smyth/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
from dataclasses import asdict, dataclass, field
from pathlib import Path
from typing import Any

import toml

Expand Down Expand Up @@ -29,7 +30,7 @@ class Config:
smyth_path_prefix: str = "/smyth"

@classmethod
def from_dict(cls, config_dict: dict):
def from_dict(cls, config_dict: dict[str, Any]) -> "Config":
handler_data = config_dict.pop("handlers")
handlers = {
handler_name: HandlerConfig(**handler_config)
Expand All @@ -48,7 +49,7 @@ def get_config_file_path(file_name: str = "pyproject.toml") -> Path:
return directory.joinpath(file_name).resolve()


def get_config_dict(config_file_name: str | None = None) -> dict:
def get_config_dict(config_file_name: str | None = None) -> dict[str, Any]:
"""Get config dict."""
if config_file_name:
config_file_path = get_config_file_path(config_file_name)
Expand All @@ -58,7 +59,7 @@ def get_config_dict(config_file_name: str | None = None) -> dict:
return toml.load(config_file_path)


def get_config(config_dict: dict) -> Config:
def get_config(config_dict: dict[str, Any]) -> Config:
"""Get config."""
if environ_config := os.environ.get("__SMYTH_CONFIG"):
config_data = json.loads(environ_config)
Expand Down
2 changes: 1 addition & 1 deletion src/smyth/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

async def generate_context_data(
request: Request | None, smyth_handler: SmythHandler, process: RunnerProcessProtocol
):
) -> dict[str, Any]:
"""
The data returned by this function is passed to the
`smyth.runner.FaneContext` as kwargs.
Expand Down
8 changes: 6 additions & 2 deletions src/smyth/event.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from typing import Any

from starlette.requests import Request

from smyth.types import EventData


async def generate_api_gw_v2_event_data(request: Request):
async def generate_api_gw_v2_event_data(request: Request) -> EventData:
source_ip = None
if request.client:
source_ip = request.client.host
Expand All @@ -28,5 +32,5 @@ async def generate_api_gw_v2_event_data(request: Request):
}


async def generate_lambda_invocation_event_data(request: Request):
async def generate_lambda_invocation_event_data(request: Request) -> Any:
return await request.json()
21 changes: 12 additions & 9 deletions src/smyth/runner/fake_context.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import sys
from collections.abc import Callable
from time import strftime, time
from typing import Any

from aws_lambda_powertools.utilities.typing import LambdaContext

Expand All @@ -10,7 +12,7 @@ def __init__(
name: str | None = None,
version: str | None = "LATEST",
timeout: int | None = None,
**kwargs,
**kwargs: Any,
):
if name is None:
name = "Fake"
Expand Down Expand Up @@ -39,31 +41,32 @@ def get_remaining_time_in_millis(self) -> int: # type: ignore[override]
)

@property
def function_name(self):
def function_name(self) -> str:
return self.name

@property
def function_version(self):
def function_version(self) -> str:
return self.version

@property
def invoked_function_arn(self):
def invoked_function_arn(self) -> str:
return "arn:aws:lambda:serverless:" + self.name

@property
def memory_limit_in_mb(self):
# This indeed is a string in the real context hence the ignore[override]
def memory_limit_in_mb(self) -> str: # type: ignore[override]
return "1024"

@property
def aws_request_id(self):
def aws_request_id(self) -> str:
return "1234567890"

@property
def log_group_name(self):
def log_group_name(self) -> str:
return "/aws/lambda/" + self.name

@property
def log_stream_name(self):
def log_stream_name(self) -> str:
return (
strftime("%Y/%m/%d")
+ "/[$"
Expand All @@ -72,5 +75,5 @@ def log_stream_name(self):
)

@property
def log(self):
def log(self) -> Callable[[str], int] | Any:
return sys.stdout.write
Loading
Loading