When writing an AWS Lambda function, it’s quite possible that you get to the point where the file becomes too big. So, what do you do? You create a second file and refactor your code to do a relative import. Then, the only step left is to deploy your code and run it. And then it fails… Have you been here before? Then I might have some sound advice for you.
If you look at the PEP 8 — Style Guide for Python Code.
You will read that absolute imports are recommended. But, explicit relative imports are an acceptable alternative to absolute imports.
This means that you should favor absolute imports. And only use relative imports within a package.
Sample code
Let’s create a hello world lambda function to show this. So we have the following code in a file called hello_world/app.py
:
import json
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": json.dumps({"message": "hello world"}),
}
For simplicity reasons I used the AWS Serverless Application Model.
The templates contain the following resource:
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.9
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
The most important thing is the Handler
definition. The value app.lambda_handler
translates to:
Use the
app.py
file and invoke thelambda_handler
method.
We will now move the payload to a file called hello_world/response.py
:
def get_response() -> dict:
return {"message": "hello world"}
And update the hello_world/app.py
:
import json
from .response import get_response
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": json.dumps(get_response()),
}
You run your test and it works, nice! So we will now deploy our code and that it also works. And it fails…
[ERROR] Runtime.ImportModuleError: Unable to import module 'app': attempted relative import with no known parent package
Traceback (most recent call last):
The content of the hello_world
folder ends up in the /var/task/
folder. And not the folder itself.
So it might look like that the code is in a package but from the Lambda runtime it’s not.
The solution
You can solve this by creating a package. How you might ask? Create a hello_world
folder in the hello_world
folder.
Move all files except the requirements.txt
into the created folder.
Next you need to update the Handler
in the template:
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: hello_world.app.lambda_handler
Runtime: python3.9
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Now the value of the Handler
translates to:
In the
hello_world
package you use theapp.py
file and invoke thelambda_handler
method.
When you deploy you will now notice that the relative import works.
Conclusion
I would always tell you to deploy your python code in a package. It makes it easier to use many files and organize your code better.
It also allows you to separate the Lambda invocation logic from your business logic. Making it easier to test and maintain.
Image by Gerd Altmann from Pixabay