I’m having a hard time figuring out the “best” layout for a lambda project. I know there are tons of similar python layout questions, however none of them quite solve my particular use case, though I don’t think what I’m trying to achieve is all that strange. The major requirements are:
- Deployable to lambda (I use terragrunt/terraform, specifically this module, though I don’t think it’s super relevant. It ultimately just zips up the directory you give it, and uploads it to lambda)
- A separate test directory that DOES NOT get uploaded to lamba (since there are strict size limitations with lambda, and it’s just good policy to keep things small)
- I can run the tests in vsCode (currently using pytest through the vsCode Testing tab).
My directory structure looks like this (simplified):
.
├── lambda.tf
├── main.tf
├── src
│ ├── init.py
│ ├── lambda_handler.py
│ ├── file1.py
│ ├── requirements.txt
│ ├── subdir
│ │ ├── init.py
│ │ ├── file2.py
│ │ ├── file3.py
├── test
│ ├── init.py
│ ├── test1.py
│ ├── requirements.txt
│ ├── subdir
│ │ ├── init.py
│ │ └── test2.py
│ │ └── test3.py
The src
directory is the one that gets zipped up and uploaded to the lambda, and it is configured to run a method in lambda_handler.py. The test directory might have it’s own, distinct requirements.txt, and does not get uploaded to lambda.
lambda_handler.py has the following import statement:
from file1 import <method>
file1.py has the following import statements:
from subdir import file1
from subdir.file2 import <method>
test1.py has the following import statement:
from src import file1
test2.py has the following import statement:
`from src.subdir import file2
I also have the following setting.json for vscode. This gets linting working, and makes it possible to run tests.
{
"jira-plugin.workingProject": "",
"python.testing.pytestArgs": [
"."
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"terminal.integrated.env.osx": {
"PYTHONPATH": "${workspaceFolder}/modules/invoices/src:${workspaceFolder}/modules/invoices/test",
},
"terminal.integrated.env.linux": {
"PYTHONPATH": "${workspaceFolder}/modules/invoices/src:${workspaceFolder}/modules/invoices/test",
},
"terminal.integrated.env.windows": {
"PYTHONPATH": "${workspaceFolder}/modules/invoices/src:${workspaceFolder}/modules/invoices/test",
},
"python.envFile": "${workspaceFolder}/.env",
"python.testing.cwd": "${workspaceFolder}/modules/invoices/test",
}
With the structure above, I can deploy to lambda and everything works as expected in the cloud. However, I get errors like the below when pytest via vscode does it’s test discovery (modified to match simplified dir structure above):
_______________ ERROR collecting test/test1.py ________________
ImportError while importing test module '<redacted>test/test1.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
../../../.pyenv/versions/3.11.5/lib/python3.11/importlib/__init__.py:126: in import_module
return _bootstrap._gcd_import(name[level:], package, level)
test/test1.py:3: in <module>
from src import file1
src/file1.py:1: in <module>
from subdir.file2 import <method>
E ModuleNotFoundError: No module named 'subdir'
I can get the test discovery (and then testing itself) to work if I change import statements to use relative paths
from .subdir.file2 import <method>
from ..subdir import file1
and the like, but then once I upload it, everything in the lambda fails with errors like the following:
Runtime.ImportModuleError: Unable to import module 'lambda_handler': attempted relative import with no known parent package
I haven’t figured out how to get both to work. I’m also not sure I need the init.py files; I added them because they get mentioned a lot in answers to similar questions, but they don’t seem to have made any difference. I’d like to remove them if they’re not helping the situation.