lambda-playwright 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: lambda_playwright
3
+ Version: 0.1.0
4
+ Summary: SDK for invoking Lambda Playwright service
5
+ Author: Hubexo
6
+ Requires-Python: >=3.8
7
+ Requires-Dist: requests>=2.25.0
8
+ Requires-Dist: requests-aws4auth>=1.1.0
9
+ Dynamic: author
10
+ Dynamic: requires-dist
11
+ Dynamic: requires-python
12
+ Dynamic: summary
@@ -0,0 +1,63 @@
1
+ # Lambda Playwright SDK
2
+
3
+ A Python SDK for interacting with the Lambda Playwright service.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install lambda-playwright
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Initialization
14
+
15
+ ```python
16
+ from lambda_playwright import LambdaPlaywright
17
+
18
+ # Initialize with environment variables (AWS_PLAYWRIGHT_FUNCTION_URL, AWS_PLAYWRIGHT_ACCESS_KEY, AWS_PLAYWRIGHT_SECRET_ACCESS)
19
+ client = LambdaPlaywright()
20
+
21
+ # Or explicitly
22
+ client = LambdaPlaywright(
23
+ function_url="https://your-function-url.lambda-url.ap-southeast-1.on.aws",
24
+ access_key_id="YOUR_ACCESS_KEY",
25
+ secret_access_key="YOUR_SECRET_KEY",
26
+ region="ap-southeast-1"
27
+ )
28
+ ```
29
+
30
+ ### Visit a Page
31
+
32
+ ```python
33
+ response = client.visit({
34
+ "url": "https://example.com",
35
+ "actions": [
36
+ {"type": "click", "selector": "button#submit"},
37
+ {"type": "wait_for_selector", "selector": ".results"}
38
+ ],
39
+ "wait_for_network_idle": True
40
+ })
41
+
42
+ print(response["html"])
43
+ ```
44
+
45
+ ### Render HTML
46
+
47
+ ```python
48
+ response = client.render_html({
49
+ "html_content": "<h1>Hello World</h1><script>document.write('Loaded');</script>",
50
+ "actions": [],
51
+ "scroll_to_bottom": True
52
+ })
53
+
54
+ print(response["html"])
55
+ ```
56
+
57
+ ## Development
58
+
59
+ To run locally against a local Lambda container:
60
+
61
+ ```python
62
+ client = LambdaPlaywright(debug=True)
63
+ ```
@@ -0,0 +1,10 @@
1
+ from .client import LambdaPlaywright
2
+ from .types import Endpoint, ActionType, VisitPagePayload, RenderHtmlPayload
3
+
4
+ __all__ = [
5
+ "LambdaPlaywright",
6
+ "Endpoint",
7
+ "ActionType",
8
+ "VisitPagePayload",
9
+ "RenderHtmlPayload",
10
+ ]
@@ -0,0 +1,141 @@
1
+ import os
2
+ import json
3
+ import logging
4
+ import requests
5
+ from typing import Optional, Dict, Union, Any
6
+ from requests_aws4auth import AWS4Auth
7
+ from .types import Endpoint, VisitPagePayload, RenderHtmlPayload
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class LambdaPlaywright:
13
+ """
14
+ Client for the Lambda Playwright service.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ function_url: Optional[str] = None,
20
+ access_key_id: Optional[str] = None,
21
+ secret_access_key: Optional[str] = None,
22
+ region: str = "ap-southeast-1",
23
+ debug: bool = False,
24
+ local_url: str = "http://localhost:9000/2015-03-31/functions/function/invocations",
25
+ ):
26
+ """
27
+ Initialize the LambdaPlaywright client.
28
+
29
+ Args:
30
+ function_url: The AWS Lambda Function URL. Defaults to AWS_PLAYWRIGHT_FUNCTION_URL env var.
31
+ access_key_id: AWS Access Key ID. Defaults to AWS_PLAYWRIGHT_ACCESS_KEY env var.
32
+ secret_access_key: AWS Secret Access Key. Defaults to AWS_PLAYWRIGHT_SECRET_ACCESS env var.
33
+ region: AWS Region. Defaults to "ap-southeast-1".
34
+ debug: If True, invoke local Lambda container. Defaults to False.
35
+ local_url: URL for local Lambda container. Defaults to standard RIE URL.
36
+ """
37
+ self.debug = debug
38
+ self.local_url = local_url
39
+ self.region = region
40
+
41
+ # Load from env if not provided
42
+ self.function_url = function_url or os.getenv("AWS_PLAYWRIGHT_FUNCTION_URL")
43
+ self.access_key_id = access_key_id or os.getenv("AWS_PLAYWRIGHT_ACCESS_KEY")
44
+ self.secret_access_key = secret_access_key or os.getenv(
45
+ "AWS_PLAYWRIGHT_SECRET_ACCESS"
46
+ )
47
+
48
+ if not self.debug:
49
+ if not self.function_url:
50
+ # We don't raise here strictly to allow instantiation if user plans to set it later or only use debug?
51
+ # But it's better to fail early if invalid config.
52
+ # However, if user only wants to use debug mode, they shouldn't need function_url.
53
+ pass
54
+
55
+ if (
56
+ not self.access_key_id or not self.secret_access_key
57
+ ) and not self.function_url:
58
+ # If function URL is missing, cannot do anything.
59
+ # If creds are missing, maybe auth is handled elsewhere (e.g. EC2 role)?
60
+ # But requests_aws4auth needs explicit creds usually unless using boto3 session to fetch them.
61
+ # We'll stick to requiring them for now as per original main.py logic.
62
+ pass
63
+
64
+ def _get_auth(self) -> Optional[AWS4Auth]:
65
+ if self.debug:
66
+ return None
67
+
68
+ if not self.access_key_id or not self.secret_access_key:
69
+ raise ValueError(
70
+ "AWS credentials must be provided or set in env vars for non-debug mode"
71
+ )
72
+
73
+ return AWS4Auth(
74
+ self.access_key_id,
75
+ self.secret_access_key,
76
+ self.region,
77
+ "lambda",
78
+ )
79
+
80
+ def _invoke(self, endpoint: Endpoint, payload: Dict[str, Any]) -> Dict[str, Any]:
81
+ """
82
+ Internal invoke method.
83
+ """
84
+ if self.debug:
85
+ url = self.local_url
86
+ # For local RIE, we emulate the rawPath behavior if needed,
87
+ # or rely on the handler finding rawPath in the body as per `main.py` convention.
88
+ full_payload = {"rawPath": endpoint.value, **payload}
89
+ else:
90
+ if not self.function_url:
91
+ raise ValueError("function_url is not configured")
92
+ url = f"{self.function_url.rstrip('/')}{endpoint.value}"
93
+ full_payload = payload
94
+
95
+ auth = self._get_auth()
96
+ headers = {"Content-Type": "application/json"}
97
+
98
+ logger.debug(f"Invoking {url} with payload {full_payload}")
99
+
100
+ response = requests.post(url, json=full_payload, headers=headers, auth=auth)
101
+
102
+ if response.status_code != 200:
103
+ # Try to parse error message if possible
104
+ try:
105
+ err_body = response.json()
106
+ if isinstance(err_body, dict) and "message" in err_body:
107
+ raise Exception(f"Lambda invocation failed: {err_body['message']}")
108
+ except Exception:
109
+ pass
110
+ raise Exception(
111
+ f"Lambda invocation failed with status {response.status_code}: {response.text}"
112
+ )
113
+
114
+ # Parse response
115
+ try:
116
+ data = response.json()
117
+ except json.JSONDecodeError:
118
+ raise Exception("Response was not valid JSON")
119
+
120
+ if "body" in data:
121
+ body_content = data["body"]
122
+ if isinstance(body_content, str):
123
+ try:
124
+ return json.loads(body_content)
125
+ except json.JSONDecodeError:
126
+ return body_content # Should probably be JSON, but if not return raw string?
127
+ return body_content
128
+
129
+ return data
130
+
131
+ def visit(self, payload: VisitPagePayload) -> Dict[str, Any]:
132
+ """
133
+ Visit a web page.
134
+ """
135
+ return self._invoke(Endpoint.VISIT, payload)
136
+
137
+ def render_html(self, payload: RenderHtmlPayload) -> Dict[str, Any]:
138
+ """
139
+ Render HTML content.
140
+ """
141
+ return self._invoke(Endpoint.RENDER_HTML, payload)
@@ -0,0 +1,49 @@
1
+ from enum import Enum
2
+ from typing import TypedDict, List, Optional, Any, Union
3
+
4
+
5
+ class Endpoint(Enum):
6
+ VISIT = "/visit"
7
+ RENDER_HTML = "/render-html"
8
+
9
+
10
+ class ActionType(str, Enum):
11
+ CLICK = "click"
12
+ WAIT_FOR_SELECTOR = "wait_for_selector"
13
+ SLEEP = "sleep"
14
+ SCROLL_TO_BOTTOM = "scroll_to_bottom"
15
+ FILL = "fill"
16
+ TYPE = "type"
17
+ EVALUATE = "evaluate"
18
+
19
+
20
+ class Action(TypedDict, total=False):
21
+ type: ActionType
22
+ selector: Optional[str]
23
+ timeout: Optional[int]
24
+ duration: Optional[float]
25
+ value: Optional[str]
26
+ text: Optional[str]
27
+ delay: Optional[float]
28
+ script: Optional[str]
29
+
30
+
31
+ class VisitPagePayload(TypedDict, total=False):
32
+ url: str
33
+ headers: Optional[dict]
34
+ cookies: Optional[dict]
35
+ user_agent: Optional[str]
36
+ actions: Optional[List[Action]]
37
+ max_retries: Optional[int]
38
+ timeout_ms: Optional[int]
39
+ wait_for_network_idle: Optional[bool]
40
+ scroll_to_bottom: Optional[bool]
41
+
42
+
43
+ class RenderHtmlPayload(TypedDict, total=False):
44
+ html_content: str
45
+ actions: Optional[List[Action]]
46
+ max_retries: Optional[int]
47
+ timeout_ms: Optional[int]
48
+ wait_for_network_idle: Optional[bool]
49
+ scroll_to_bottom: Optional[bool]
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: lambda_playwright
3
+ Version: 0.1.0
4
+ Summary: SDK for invoking Lambda Playwright service
5
+ Author: Hubexo
6
+ Requires-Python: >=3.8
7
+ Requires-Dist: requests>=2.25.0
8
+ Requires-Dist: requests-aws4auth>=1.1.0
9
+ Dynamic: author
10
+ Dynamic: requires-dist
11
+ Dynamic: requires-python
12
+ Dynamic: summary
@@ -0,0 +1,10 @@
1
+ README.md
2
+ setup.py
3
+ lambda_playwright/__init__.py
4
+ lambda_playwright/client.py
5
+ lambda_playwright/types.py
6
+ lambda_playwright.egg-info/PKG-INFO
7
+ lambda_playwright.egg-info/SOURCES.txt
8
+ lambda_playwright.egg-info/dependency_links.txt
9
+ lambda_playwright.egg-info/requires.txt
10
+ lambda_playwright.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ requests>=2.25.0
2
+ requests-aws4auth>=1.1.0
@@ -0,0 +1 @@
1
+ lambda_playwright
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,14 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="lambda_playwright",
5
+ version="0.1.0",
6
+ description="SDK for invoking Lambda Playwright service",
7
+ author="Hubexo",
8
+ packages=find_packages(),
9
+ install_requires=[
10
+ "requests>=2.25.0",
11
+ "requests-aws4auth>=1.1.0",
12
+ ],
13
+ python_requires=">=3.8",
14
+ )