Skip to content
29 changes: 27 additions & 2 deletions aws_lambda_powertools/utilities/parser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
"""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}")


def __dir__() -> list[str]:
return sorted(set(globals()) | set(__all__))


__all__ = [
"event_parser",
"parse",
Expand Down
31 changes: 31 additions & 0 deletions tests/functional/parser/_parser_import_probe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import importlib
import inspect
import sys

ENVELOPE_MODULE = "aws_lambda_powertools.utilities.parser.envelopes"
scenario = sys.argv[1]

if scenario == "lazy":
parser = importlib.import_module("aws_lambda_powertools.utilities.parser")

assert not any(module == ENVELOPE_MODULE or module.startswith(f"{ENVELOPE_MODULE}.") for module in sys.modules)
assert {"envelopes", "BaseEnvelope"} <= set(dir(parser))
assert ENVELOPE_MODULE not in sys.modules

members = dict(inspect.getmembers(parser))
assert members["envelopes"] is parser.envelopes
assert members["BaseEnvelope"] is parser.BaseEnvelope
assert ENVELOPE_MODULE in sys.modules
elif scenario == "star":
from aws_lambda_powertools.utilities.parser import * # noqa: E402,F403

expected = {
"parse",
"event_parser",
"envelopes",
"BaseEnvelope",
}
assert expected <= globals().keys()
assert ENVELOPE_MODULE in sys.modules
else:
raise ValueError(f"Unknown scenario: {scenario}")
23 changes: 23 additions & 0 deletions tests/functional/parser/test_parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import json
import os
import subprocess
import sys
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Literal, Union

import pydantic
Expand Down Expand Up @@ -308,3 +312,22 @@ def handler(event: SqsModel, _: LambdaContext):
assert parsed_event[0].version == "version"

handler(event, LambdaContext())


@pytest.mark.parametrize("scenario", ["lazy", "star"])
def test_parser_imports_in_clean_interpreter(scenario):
project_root = Path(__file__).parents[3]
probe = Path(__file__).with_name("_parser_import_probe.py")
env = os.environ.copy()
env["PYTHONPATH"] = str(project_root)

result = subprocess.run(
[sys.executable, str(probe), scenario],
cwd=project_root,
env=env,
capture_output=True,
text=True,
check=False,
)

assert result.returncode == 0, result.stderr