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 theapp.pyfile and invoke thelambda_handlermethod.
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 thehello_worldpackage you use theapp.pyfile and invoke thelambda_handlermethod.
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
Written by

Joris Conijn
Joris is the AWS Practise CTO of the Xebia Cloud service line and has been working with the AWS cloud since 2009 and focussing on building event-driven architectures. While working with the cloud from (almost) the start, he has seen most of the services being launched. Joris strongly believes in automation and infrastructure as code and is open to learning new things and experimenting with them because that is the way to learn and grow.
Our Ideas

