Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions aws_lambda_powertools/utilities/parser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
"""Advanced event_parser utility"""

from __future__ import annotations

import importlib
from typing import TYPE_CHECKING

from pydantic import BaseModel, Field, ValidationError, field_validator, model_validator

from aws_lambda_powertools.utilities.parser import envelopes
from aws_lambda_powertools.utilities.parser.envelopes import BaseEnvelope
from aws_lambda_powertools.utilities.parser.parser import event_parser, parse

if TYPE_CHECKING:
from aws_lambda_powertools.utilities.parser import envelopes as envelopes
from aws_lambda_powertools.utilities.parser.envelopes import BaseEnvelope


def __getattr__(name: str) -> object:
if name == "envelopes":
_envelopes = importlib.import_module(f"{__name__}.envelopes")
globals()[name] = _envelopes
return _envelopes
if name == "BaseEnvelope":
_envelopes_module = importlib.import_module(f"{__name__}.envelopes")
_base_envelope = _envelopes_module.BaseEnvelope
globals()[name] = _base_envelope
return _base_envelope
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")


__all__ = [
"event_parser",
"parse",
Expand Down
78 changes: 78 additions & 0 deletions tests/functional/parser/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,81 @@ def handler(event: SqsModel, _: LambdaContext):
assert parsed_event[0].version == "version"

handler(event, LambdaContext())


def test_parser_import_does_not_eagerly_load_envelopes():
"""Importing parse from parser __init__ must not eagerly load all envelope modules.

Envelopes are only needed when envelope= is passed to parse()/event_parser().
Eager loading all 16 envelopes adds ~900ms to Lambda cold start for functions
that only use parse() without an envelope.

This test runs in a fresh subprocess to ensure reliable isolation from any
modules already loaded by conftest or other tests.
"""
import subprocess
import sys

# Test 1: importing parse alone should NOT load envelopes
script_parse_only = """
import sys
from aws_lambda_powertools.utilities.parser import parse
envelope_modules = [key for key in sys.modules if "aws_lambda_powertools.utilities.parser.envelopes" in key]
assert not envelope_modules, f"Envelope modules loaded on parse import: {envelope_modules}"
print("PASS: parse import does not load envelopes")
"""

result = subprocess.run([sys.executable, "-c", script_parse_only], capture_output=True, text=True, check=False)
assert result.returncode == 0, f"parse import test failed:\n{result.stderr}\n{result.stdout}"

# Test 2: importing envelopes explicitly SHOULD load envelopes
script_envelopes_import = """
import sys
from aws_lambda_powertools.utilities.parser import envelopes
assert "aws_lambda_powertools.utilities.parser.envelopes" in sys.modules, "envelopes module not loaded"
assert hasattr(envelopes, "SqsEnvelope"), "envelopes.SqsEnvelope not accessible"
print("PASS: envelopes import works correctly")
"""

result = subprocess.run(
[sys.executable, "-c", script_envelopes_import],
capture_output=True,
text=True,
check=False,
)
assert result.returncode == 0, f"envelopes import test failed:\n{result.stderr}\n{result.stdout}"

# Test 3: importing BaseEnvelope explicitly SHOULD load envelopes
script_base_envelope_import = """
import sys
from aws_lambda_powertools.utilities.parser import BaseEnvelope
assert "aws_lambda_powertools.utilities.parser.envelopes" in sys.modules, "envelopes module not loaded"
assert BaseEnvelope.__name__ == "BaseEnvelope", "BaseEnvelope not correctly imported"
print("PASS: BaseEnvelope import works correctly")
"""

result = subprocess.run(
[sys.executable, "-c", script_base_envelope_import],
capture_output=True,
text=True,
check=False,
)
assert result.returncode == 0, f"BaseEnvelope import test failed:\n{result.stderr}\n{result.stdout}"

# Test 4: from parser import * should work and load all public exports
script_import_star = """
from aws_lambda_powertools.utilities.parser import *
assert 'parse' in dir(), "parse not available after import *"
assert 'event_parser' in dir(), "event_parser not available after import *"
assert 'envelopes' in dir(), "envelopes not available after import *"
assert 'BaseEnvelope' in dir(), "BaseEnvelope not available after import *"
print("PASS: import * works correctly")
"""

result = subprocess.run(
[sys.executable, "-c", script_import_star],
capture_output=True,
text=True,
check=False,
)
assert result.returncode == 0, f"import * test failed:\n{result.stderr}\n{result.stdout}"