{"id":14848260,"url":"https://github.com/CloudSnorkel/lovage","last_synced_at":"2025-09-18T04:33:03.800Z","repository":{"id":50170832,"uuid":"236130073","full_name":"CloudSnorkel/lovage","owner":"CloudSnorkel","description":"Kind of like Celery, but simpler and with more Lambda","archived":false,"fork":false,"pushed_at":"2023-10-17T22:18:39.000Z","size":89,"stargazers_count":10,"open_issues_count":2,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-12-13T03:53:46.795Z","etag":null,"topics":["aws","lambda","python","python-library","serverless"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CloudSnorkel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-01-25T05:31:15.000Z","updated_at":"2024-10-16T15:11:13.000Z","dependencies_parsed_at":"2024-01-07T00:08:50.651Z","dependency_job_id":null,"html_url":"https://github.com/CloudSnorkel/lovage","commit_stats":{"total_commits":21,"total_committers":2,"mean_commits":10.5,"dds":0.04761904761904767,"last_synced_commit":"6596fc2fd7fad6ae5b50c1fb55d299d8fc17278d"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CloudSnorkel%2Flovage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CloudSnorkel%2Flovage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CloudSnorkel%2Flovage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CloudSnorkel%2Flovage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CloudSnorkel","download_url":"https://codeload.github.com/CloudSnorkel/lovage/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233451329,"owners_count":18678210,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","lambda","python","python-library","serverless"],"created_at":"2024-09-19T13:01:50.770Z","updated_at":"2025-09-18T04:32:58.518Z","avatar_url":"https://github.com/CloudSnorkel.png","language":"Python","funding_links":[],"categories":["Frameworks"],"sub_categories":[],"readme":"# Lovage\n\n[![Actions Status](https://github.com/CloudSnorkel/lovage/workflows/Lovage%20build/badge.svg)](https://github.com/CloudSnorkel/lovage/actions)\n[![PyPI](https://badge.fury.io/py/lovage.svg)](https://badge.fury.io/py/lovage)\n[![PyPI pyversions](https://img.shields.io/pypi/pyversions/lovage.svg)](https://pypi.org/project/lovage/)\n[![PyPI status](https://img.shields.io/pypi/status/lovage.svg)](https://pypi.org/project/lovage/)\n[![GitHub stars](https://img.shields.io/github/stars/CloudSnorkel/lovage.svg?style=social\u0026label=Star\u0026maxAge=2592000)](https://GitHub.com/CloudSnorkel/lovage/stargazers/)\n\nPython-only serverless library that's more RPC-like and less HTTP service oriented.\n\n**Status:** Usable but not battle tested. PRs are welcome!\n\n## Installation\n\nLovage is a Python library with no external dependencies. Just install and use.\n\n````bash\npip install lovage\n````\n\nYou will have to set up your AWS credentials to deploy with either [environment variables](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#environment-variables), [shared credentials files](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#shared-credentials-file) or [any other method](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html) that works with boto3.\n\n## Overview\n\nLovage is a Python 3 library that makes it very easy to offload normal Python functions to the cloud using AWS Lambda\nfunctions.\n\n### Call Functions Easily\n\nLovage lets you call functions without knowing anything about AWS API. You define the function as part of your codebase,\nuse `@app.task` decorator, deploy it, and then just call the function with `.invoke()` or `invoke_async()`. Function\narguments, return values, and exceptions can still be used as usual. You don't need to worry about serialization or AWS\nAPI. Everything just works as it normally does with normal Python functions.\n\n**Note:** exceptions are only supported when using `PickleSerializer`. With the default `JSONSerializer` all exceptions\nare converted to `LovageRemoteException`.\n\n```python\nimport lovage.backends\n\napp = lovage.Lovage(lovage.backends.AwsLambdaBackend(\"lovage-test\"))\n\n\n@app.task\ndef hello(x):\n    return x + 1\n\nif __name__ == \"__main__\":\n    app.deploy(root=\".\", requirements=[\"requests\"])\n    print(\"hello.invoke(1) returned\", hello.invoke(1))\n```\n\n### Compartmentalize Functions\n\nIt's easy to define separate IAM policies for each function to enhance your security with compartmentalization. You can\ngive granular access to each function to just the resources it needs.\n\n```python\nimport boto3\nimport lovage.backends\nimport os.path\n\napp = lovage.Lovage(lovage.backends.AwsLambdaBackend(\"lovage-test\"))\n\n# let this function send emails using SES as info@cloudsnorkel.com\nEMAIL_POLICY = {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"ses:SendEmail\",\n            ],\n            \"Resource\": \"*\",\n            \"Condition\": {\n                \"StringEquals\": {\n                    \"ses: FromAddress\": \"info@cloudsnorkel.com\"\n                }\n            }\n        }\n    ]\n}\n\n@app.task(aws_policies=[EMAIL_POLICY])\ndef send_email(x):\n    boto3.client(\"ses\").send_email(Source=\"info@cloudsnorkel.com\", ...)\n\nif __name__ == \"__main__\":\n    app.deploy(root=os.path.dirname(__file__), requirements=[\"boto3==1.12.25\"])\n    send_email.invoke_async()\n```\n\n### Requirements Layer Generated in Lambda\n\nUnlike other solutions, Lovage collects and packages required libraries in Lambda itself. Each deployment has a custom\nresource that gets the requirements list as a parameter, downloads all of them in Lambda, uploads it directly to S3, and\nfinally creates a Lambda layer containing all the dependencies. This gives you:\n\n* Much faster cloud-local dependencies downloads and uploads\n* No local development dependencies but Python (no need for Docker, no need to run on Linux, etc.)\n* Faster code updates as you don't have to zip up the requirements and upload them along with your code\n* Cleaner working directory with no dependencies being duplicated from your `site-packages` and no hidden folders\n\n```python\nimport boto3\nimport lovage.backends\n\napp = lovage.Lovage(lovage.backends.AwsLambdaBackend(\"lovage-test\"))\n\nif __name__ == \"__main__\":\n    app.deploy(requirements=[\"boto3==1.12.25\", \"requests\", \"Django\u003e=2.0.0\"])\n    # or...\n    app.deploy(requirements=open(\"requirements.txt\").read())\n```\n\n### Other Features\n\n* CloudFormation stack leaves nothing behind and can be deleted without any special treatment\n* Easy to test locally without deploying anything\n* No need for Node.js\n* Versatile configuration in code\n\n## Usage\n\nThis script will deploy one function to AWS using Lambda, S3 and CloudFormation. It will then execute the function\ntwice. At first it will wait for the function to finish and print its answer. Then it will execute it asynchronously and\nreturn control to your script immediately. \n\n```python\nimport lovage\nimport lovage.backends\n\napp = lovage.Lovage(lovage.backends.AwsLambdaBackend(\"lovage-test\"))\n\n\n@app.task\ndef hello(x):\n    print(\"hello world!\")\n    return x + 1\n\n\nif __name__ == \"__main__\":\n    app.deploy(requirements=[])\n    print(\"hello.invoke(1) returned\", hello.invoke(1))\n    hello.invoke_async(2)\n```\n\nTo delete the functions, simply delete the `lovage-test` CloudFormation stack. You can choose the name when creating the\n`AwsLambdaBackend` object.\n\n### Testing Locally\n\nSometimes you don't want to wait for a full deployment and just want to iterate locally. Lovage makes this simple with\n`LocalBackend` which is the default backend. `app.deploy()` will do nothing and any function call will be executed\nlocally. When using `invoke_async()` a new thread will be created and the function will execute there.\n\n```python\nimport platform\n\nimport lovage\n\napp = lovage.Lovage()\n\n\n@app.task\ndef hello():\n    print(\"Hello locally from\", platform.node())\n\n\nif __name__ == \"__main__\":\n    app.deploy()  # doesn't do anything\n    hello.invoke()\n```\n\n### Ignoring Files\n\nLovage will package all files from the current working directory for the Lambda function and upload them for you. If you\nwant to avoid including some files because they are not required, you can create a file named `.lovageignore` which\nworks just like ce`.gitignore`. Any pattern listed there will be excluded from the package.\n\n### Separate Environments\n\nA common use-case in cloud development is having a separate environment for development, QA and production. Sometimes\neven a separate environment for each developer. Lovage uses a self-contained CloudFormation stack for each environment.\nThere are no local or remote side-effects to worry about. As soon as you delete the stack, everything is gone.\n\nThe environment name is set by the first parameter given to `AwsLambdaBackend()`.\n\n```python\napp_dev = lovage.Lovage(lovage.backends.AwsLambdaBackend(\"lovage-dev\"))\napp_prod = lovage.Lovage(lovage.backends.AwsLambdaBackend(\"lovage-prod\"))\n```\n\n*Caveat*: if you use `AwsLambdaBackend.add_resource()` to add additional CloudFormation resources to your stack, you may\nhave to delete those manually. For example, if you add a bucket, you have to make sure it's empty before deleting the\nstack.\n\n## Available Configuration\n\nConfiguration can be passed to the `@app.task()` decorator. For example:\n\n```python\n@app.task(timeout=30)\ndef hello_world():\n  return 42\n```\n\nSome configuration is platform-specific and will therefore have a prefix like `aws_`.\n\n| Configuration | Purpose | Default Value |\n| ------------- |---------------|-------|\n| `timeout` | Set Lambda timeout in seconds. Every Lambda function has a maximum execution time. | `3` |\n| `aws_policies` | List of IAM policy documents to attach to the Lambda function. | `[]` |\n| `aws_vpc_subnet_ids` | List of VPC subnets to attach to the Lambda function. Must be used together with `aws_vpc_security_group_ids`. | `[]` |\n| `aws_vpc_security_group_ids` | List of VPC security groups to attach to the Lambda function. Must be used along with `aws_vpc_subnet_ids`. | `[]` |\n\n## Best Practices\n\n* Always specify `root` so you are sure which files are packaged. You can use something like `from pathlib import Path;\n  app.deploy(root=Path(__file__).parent.parent)` to easily get your root folder.\n* Always use `if __name__ == \"__main__\":` in files with Lovage tasks. Global code will be executed both locally and in\n  Lambda. This may cause some unwanted side-effects.\n* You should probably have a separate script to call `app.deploy()`. No-op deploys are pretty quick, but still take time\n  to zip up the code, check if the latest is already available on S3, and finally update the CloudFormation stack.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCloudSnorkel%2Flovage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCloudSnorkel%2Flovage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCloudSnorkel%2Flovage/lists"}