Skip to content

Commit 53cbb16

Browse files
Improve logging (#7)
* Improve logging * Add POC script
1 parent d8b1f3f commit 53cbb16

File tree

14 files changed

+121
-12
lines changed

14 files changed

+121
-12
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This tool builds a ZIP file from a virtual environment with all depedencies inst
66
then this tool will employ the ZIP-inside-ZIP (nested-ZIP) workaround. This allows deploying Lambdas with large
77
dependency packages, especially those with native code compiled extensions like Pandas, PyArrow, etc.
88

9-
This technique was originally pioneered by [serverless-python-requirements](https://github.com/serverless/serverless-python-requirements), which is a NodeJS (JavaScript) plugin for the [Serverless Framework](https://github.com/serverless/serverless). This technique has been improved here to not require any special imports in your entrypoint source file. That is, no changes are needed to your source code to leverage the nested ZIP deployment.
9+
This technique was originally pioneered by [serverless-python-requirements](https://github.com/serverless/serverless-python-requirements), which is a NodeJS (JavaScript) plugin for the [Serverless Framework](https://github.com/serverless/serverless). The technique has been improved here to not require any special imports in your entrypoint source file. That is, no changes are needed to your source code to leverage the nested ZIP deployment.
1010

1111
The motivation for this Python tool is to achieve the same results as serverless-python-requirements but with a
1212
purely Python tool. This can simplify and speed up developer and CI/CD workflows.

package_python_function/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import argparse
22
from pathlib import Path
3+
import logging
4+
import sys
35

46
from .packager import Packager
57

68

79
def main() -> None:
10+
logging.basicConfig(level=logging.INFO, stream=sys.stdout, format="%(message)s")
11+
812
args = parse_args()
913
project_path = Path(args.project).resolve()
1014
venv_path = Path(args.venv_dir).resolve()

package_python_function/packager.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
from tempfile import NamedTemporaryFile
33
import zipfile
44
import shutil
5+
import logging
56

67
from .python_project import PythonProject
78

89

10+
logger = logging.getLogger(__name__)
11+
12+
913
class Packager:
1014
AWS_LAMBDA_MAX_UNZIP_SIZE = 262144000
1115

@@ -26,19 +30,15 @@ def input_path(self) -> Path:
2630
return python_paths[0] / 'site-packages'
2731

2832
def package(self) -> None:
29-
# TODO: Improve logging.
30-
print("Packaging:", self.project.path)
31-
print("Output:", self.output_file)
32-
print("Input:", self.input_path)
33-
print("Entrypoint Package name:", self.project.entrypoint_package_name)
33+
logger.info(f"Packaging: '{self.input_path}' to '{self.output_file}' using '{self.project.path}'... ")
3434

3535
self.output_dir.mkdir(parents=True, exist_ok=True)
3636

37-
with NamedTemporaryFile() as dependencies_zip:
37+
with NamedTemporaryFile(suffix=".zip") as dependencies_zip:
3838
self.zip_all_dependencies(Path(dependencies_zip.name))
3939

4040
def zip_all_dependencies(self, target_path: Path) -> None:
41-
print(f"Zipping to {target_path} ...")
41+
logger.info(f"Zipping to {target_path}...")
4242

4343
with zipfile.ZipFile(target_path, 'w', zipfile.ZIP_DEFLATED) as zip_file:
4444
def zip_dir(path: Path) -> None:
@@ -53,20 +53,22 @@ def zip_dir(path: Path) -> None:
5353

5454
compressed_bytes = target_path.stat().st_size
5555

56-
print(f"Uncompressed size: {self._uncompressed_bytes:,} bytes")
57-
print(f"Compressed size: {compressed_bytes:,} bytes")
56+
logger.info(f"Uncompressed size: {self._uncompressed_bytes:,} bytes. Compressed size: {compressed_bytes:,} bytes.")
5857

5958
if self._uncompressed_bytes > self.AWS_LAMBDA_MAX_UNZIP_SIZE:
60-
print(f"The uncompressed size of the ZIP file is greater than the AWS Lambda limit of {self.AWS_LAMBDA_MAX_UNZIP_SIZE:,} bytes.")
59+
logger.info(f"The uncompressed size of the ZIP file is greater than the AWS Lambda limit of {self.AWS_LAMBDA_MAX_UNZIP_SIZE:,} bytes.")
6160
if(compressed_bytes < self.AWS_LAMBDA_MAX_UNZIP_SIZE):
62-
print(f"The compressed size ({compressed_bytes:,}) is less than the AWS limit, so the nested-zip strategy will be used.")
61+
logger.info(f"The compressed size ({compressed_bytes:,}) is less than the AWS limit, so the nested-zip strategy will be used.")
6362
self.generate_nested_zip(target_path)
6463
else:
6564
print(f"TODO Error. The unzipped size it too large for AWS Lambda.")
6665
else:
66+
logger.info(f"Copying '{target_path}' to '{self.output_file}'")
6767
shutil.copy(str(target_path), str(self.output_file))
6868

6969
def generate_nested_zip(self, inner_zip_path: Path) -> None:
70+
logger.info(f"Generating nested-zip and __init__.py loader using entrypoint package '{self.project.entrypoint_package_name}'...")
71+
7072
with zipfile.ZipFile(self.output_file, 'w') as outer_zip_file:
7173
entrypoint_dir = Path(self.project.entrypoint_package_name)
7274
outer_zip_file.write(

scripts/poc/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.test

scripts/poc/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This is the original proof-of-concept script used to work out the nested-ZIP automatic extraction during Lambda INIT

scripts/poc/inner_package/other_package/__init__.py

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def other_package_module():
2+
print("other_package_module")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# This file represents the original module's __init__.py file that gets renamed when creating the innner ZIP.
2+
3+
print("__init__ original")
4+
5+
GLOBAL_VALUE_IN_INIT_ORIGINAL = "This global is defined in the original __init__.py"
6+
7+
from .other_module import other_module_function
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
print("main.py: Load")
2+
3+
from zip_in_zip_test import GLOBAL_VALUE_IN_INIT_ORIGINAL, other_module_function
4+
from other_package.other_package_module import other_package_module
5+
6+
def main():
7+
print("Hello from main!")
8+
print(GLOBAL_VALUE_IN_INIT_ORIGINAL)
9+
other_module_function()
10+
other_package_module()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def other_module_function():
2+
print("I'm in other_module_function")

0 commit comments

Comments
 (0)