grasp-sdk 0.1.4__tar.gz → 0.2.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.
Potentially problematic release.
This version of grasp-sdk might be problematic. Click here for more details.
- grasp_sdk-0.2.0/PKG-INFO +291 -0
- grasp_sdk-0.2.0/README.md +246 -0
- grasp_sdk-0.2.0/RELEASE.md +109 -0
- grasp_sdk-0.2.0/TESTING.md +55 -0
- grasp_sdk-0.2.0/debug_test.py +67 -0
- grasp_sdk-0.2.0/examples/async_usage.py +69 -0
- grasp_sdk-0.2.0/examples/basic_usage.py +57 -0
- grasp_sdk-0.2.0/examples/playwright_integration.py +86 -0
- grasp_sdk-0.2.0/package.json +29 -0
- grasp_sdk-0.2.0/publish.sh +91 -0
- grasp_sdk-0.2.0/pyproject.toml +127 -0
- grasp_sdk-0.2.0/run_tests.sh +58 -0
- grasp_sdk-0.2.0/src/grasp/__init__.py +80 -0
- grasp_sdk-0.2.0/src/grasp/_async_client.py +141 -0
- grasp_sdk-0.2.0/src/grasp/_browser.py +38 -0
- grasp_sdk-0.2.0/src/grasp/_client.py +135 -0
- grasp_sdk-0.2.0/src/grasp/_container.py +222 -0
- grasp_sdk-0.2.0/src/grasp/_exceptions.py +156 -0
- grasp_sdk-0.2.0/src/grasp/_http.py +195 -0
- grasp_sdk-0.2.0/src/grasp/_models.py +47 -0
- grasp_sdk-0.2.0/src/grasp/_types.py +26 -0
- grasp_sdk-0.2.0/src/grasp/_version.py +3 -0
- grasp_sdk-0.2.0/src/grasp/py.typed +0 -0
- grasp_sdk-0.2.0/src/grasp_sdk.egg-info/PKG-INFO +291 -0
- grasp_sdk-0.2.0/src/grasp_sdk.egg-info/SOURCES.txt +31 -0
- grasp_sdk-0.2.0/src/grasp_sdk.egg-info/requires.txt +23 -0
- grasp_sdk-0.2.0/src/grasp_sdk.egg-info/top_level.txt +1 -0
- grasp_sdk-0.2.0/test_cleanup.py +56 -0
- grasp_sdk-0.2.0/tests/conftest.py +83 -0
- grasp_sdk-0.2.0/tests/test_async_client.py +308 -0
- grasp_sdk-0.2.0/tests/test_client.py +286 -0
- grasp_sdk-0.1.4/MANIFEST.in +0 -30
- grasp_sdk-0.1.4/PKG-INFO +0 -307
- grasp_sdk-0.1.4/README.md +0 -260
- grasp_sdk-0.1.4/build_and_publish.py +0 -135
- grasp_sdk-0.1.4/example_usage.py +0 -101
- grasp_sdk-0.1.4/grasp_sdk/__init__.py +0 -296
- grasp_sdk-0.1.4/grasp_sdk/models/__init__.py +0 -78
- grasp_sdk-0.1.4/grasp_sdk/sandbox/chrome-stable.mjs +0 -394
- grasp_sdk-0.1.4/grasp_sdk/sandbox/chromium.mjs +0 -389
- grasp_sdk-0.1.4/grasp_sdk/services/__init__.py +0 -8
- grasp_sdk-0.1.4/grasp_sdk/services/browser.py +0 -405
- grasp_sdk-0.1.4/grasp_sdk/services/sandbox.py +0 -586
- grasp_sdk-0.1.4/grasp_sdk/utils/__init__.py +0 -31
- grasp_sdk-0.1.4/grasp_sdk/utils/auth.py +0 -227
- grasp_sdk-0.1.4/grasp_sdk/utils/config.py +0 -150
- grasp_sdk-0.1.4/grasp_sdk/utils/logger.py +0 -233
- grasp_sdk-0.1.4/grasp_sdk.egg-info/PKG-INFO +0 -307
- grasp_sdk-0.1.4/grasp_sdk.egg-info/SOURCES.txt +0 -41
- grasp_sdk-0.1.4/grasp_sdk.egg-info/entry_points.txt +0 -2
- grasp_sdk-0.1.4/grasp_sdk.egg-info/not-zip-safe +0 -1
- grasp_sdk-0.1.4/grasp_sdk.egg-info/requires.txt +0 -21
- grasp_sdk-0.1.4/grasp_sdk.egg-info/top_level.txt +0 -2
- grasp_sdk-0.1.4/py.typed +0 -2
- grasp_sdk-0.1.4/pyproject.toml +0 -127
- grasp_sdk-0.1.4/requirements.txt +0 -15
- grasp_sdk-0.1.4/setup.py +0 -79
- grasp_sdk-0.1.4/test_install.py +0 -87
- {grasp_sdk-0.1.4 → grasp_sdk-0.2.0}/setup.cfg +0 -0
- {grasp_sdk-0.1.4 → grasp_sdk-0.2.0/src}/grasp_sdk.egg-info/dependency_links.txt +0 -0
grasp_sdk-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: grasp-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Official Python SDK for the Grasp browser automation platform
|
|
5
|
+
Author-email: Grasp Team <dev@getgrasp.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://getgrasp.ai
|
|
8
|
+
Project-URL: Documentation, https://docs.getgrasp.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/getgrasp-ai/grasp
|
|
10
|
+
Project-URL: Issues, https://github.com/getgrasp-ai/grasp/issues
|
|
11
|
+
Keywords: browser,automation,chrome,devtools,cdp,playwright,puppeteer
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Intended Audience :: Developers
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: httpx>=0.24.0
|
|
26
|
+
Requires-Dist: pydantic>=2.0
|
|
27
|
+
Requires-Dist: pydantic-core>=2.0
|
|
28
|
+
Requires-Dist: python-dateutil>=2.8
|
|
29
|
+
Requires-Dist: typing-extensions>=4.0; python_version < "3.10"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.20; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-httpx>=0.22; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-sugar>=0.9.7; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-watch>=4.2.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-xdist>=3.0; extra == "dev"
|
|
37
|
+
Requires-Dist: rich>=13.0; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
39
|
+
Requires-Dist: playwright>=1.40; extra == "dev"
|
|
40
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
41
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
42
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
43
|
+
Requires-Dist: build>=0.10; extra == "dev"
|
|
44
|
+
Requires-Dist: twine>=4.0; extra == "dev"
|
|
45
|
+
|
|
46
|
+
# Grasp Python SDK
|
|
47
|
+
|
|
48
|
+
Official Python SDK for the Grasp browser automation platform.
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
- **Dual Client Architecture**: Both synchronous and asynchronous clients
|
|
53
|
+
- **Type Safety**: Full type hints and Pydantic models for validation
|
|
54
|
+
- **Browser Automation**: Seamless integration with Playwright and Puppeteer
|
|
55
|
+
- **Proxy Support**: Built-in proxy configuration for different use cases
|
|
56
|
+
- **Error Handling**: Comprehensive exception hierarchy
|
|
57
|
+
- **Auto-retry**: Automatic retry with exponential backoff
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install grasp-sdk
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Quick Start
|
|
66
|
+
|
|
67
|
+
### Synchronous Usage
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from grasp import Grasp
|
|
71
|
+
|
|
72
|
+
# Initialize client
|
|
73
|
+
client = Grasp(api_key="your-api-key") # or set GRASP_API_KEY env var
|
|
74
|
+
|
|
75
|
+
# Create a container
|
|
76
|
+
container = client.create(
|
|
77
|
+
idle_timeout=30000, # 30 seconds
|
|
78
|
+
proxy={
|
|
79
|
+
"enabled": True,
|
|
80
|
+
"type": "residential",
|
|
81
|
+
"country": "US"
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Access browser information
|
|
86
|
+
print(f"Container ID: {container.id}")
|
|
87
|
+
print(f"CDP WebSocket: {container.browser.ws_endpoint}")
|
|
88
|
+
print(f"Live View: {container.browser.live_url}")
|
|
89
|
+
|
|
90
|
+
# Shutdown when done
|
|
91
|
+
container.shutdown()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Asynchronous Usage
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
import asyncio
|
|
98
|
+
from grasp import AsyncGrasp
|
|
99
|
+
|
|
100
|
+
async def main():
|
|
101
|
+
# Use context manager for automatic cleanup
|
|
102
|
+
async with AsyncGrasp(api_key="your-api-key") as client:
|
|
103
|
+
# Create container
|
|
104
|
+
container = await client.create(idle_timeout=30000)
|
|
105
|
+
|
|
106
|
+
# Use the container
|
|
107
|
+
print(f"Container ID: {container.id}")
|
|
108
|
+
|
|
109
|
+
# Shutdown
|
|
110
|
+
await container.shutdown()
|
|
111
|
+
|
|
112
|
+
asyncio.run(main())
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Playwright Integration
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from grasp import Grasp
|
|
119
|
+
from playwright.sync_api import sync_playwright
|
|
120
|
+
|
|
121
|
+
# Create Grasp container
|
|
122
|
+
grasp = Grasp()
|
|
123
|
+
container = grasp.create()
|
|
124
|
+
|
|
125
|
+
# Connect Playwright
|
|
126
|
+
with sync_playwright() as p:
|
|
127
|
+
browser = p.chromium.connect_over_cdp(container.browser.ws_endpoint)
|
|
128
|
+
page = browser.new_page()
|
|
129
|
+
page.goto("https://example.com")
|
|
130
|
+
|
|
131
|
+
# Your automation code here
|
|
132
|
+
title = page.title()
|
|
133
|
+
print(f"Page title: {title}")
|
|
134
|
+
|
|
135
|
+
browser.close()
|
|
136
|
+
|
|
137
|
+
# Cleanup
|
|
138
|
+
container.shutdown()
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Configuration
|
|
142
|
+
|
|
143
|
+
### Environment Variables
|
|
144
|
+
|
|
145
|
+
- `GRASP_API_KEY`: Your API key (alternative to passing in code)
|
|
146
|
+
- `GRASP_BASE_URL`: API base URL (defaults to `https://api.getgrasp.ai`)
|
|
147
|
+
|
|
148
|
+
### Client Options
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
client = Grasp(
|
|
152
|
+
api_key="your-api-key",
|
|
153
|
+
base_url="https://api.getgrasp.ai", # optional
|
|
154
|
+
timeout=60.0, # request timeout in seconds
|
|
155
|
+
max_retries=2 # maximum retry attempts
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Container Options
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
container = client.create(
|
|
163
|
+
idle_timeout=30000, # milliseconds before container sleeps
|
|
164
|
+
proxy={
|
|
165
|
+
"enabled": True,
|
|
166
|
+
"type": "residential", # mobile, residential, isp, datacenter, custom
|
|
167
|
+
"country": "US", # optional: country code
|
|
168
|
+
"state": "CA", # optional: state code
|
|
169
|
+
"city": "LA" # optional: city name
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## API Reference
|
|
175
|
+
|
|
176
|
+
### Main Classes
|
|
177
|
+
|
|
178
|
+
#### `Grasp` / `AsyncGrasp`
|
|
179
|
+
Main client classes for interacting with the API.
|
|
180
|
+
|
|
181
|
+
**Methods:**
|
|
182
|
+
- `create(**options)`: Create a new container
|
|
183
|
+
- `connect(container_id)`: Connect to existing container
|
|
184
|
+
|
|
185
|
+
#### `GraspContainer` / `AsyncGraspContainer`
|
|
186
|
+
Represents a browser automation container.
|
|
187
|
+
|
|
188
|
+
**Properties:**
|
|
189
|
+
- `id`: Container identifier
|
|
190
|
+
- `status`: Current status (running, stopped, sleeping)
|
|
191
|
+
- `created_at`: Creation timestamp
|
|
192
|
+
- `browser`: Browser session information
|
|
193
|
+
|
|
194
|
+
**Methods:**
|
|
195
|
+
- `shutdown()`: Shut down the container
|
|
196
|
+
|
|
197
|
+
#### `BrowserSession`
|
|
198
|
+
Browser session information.
|
|
199
|
+
|
|
200
|
+
**Properties:**
|
|
201
|
+
- `ws_endpoint`: Chrome DevTools Protocol WebSocket endpoint
|
|
202
|
+
- `live_url`: Live view URL for observing the browser
|
|
203
|
+
|
|
204
|
+
### Exception Handling
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from grasp import (
|
|
208
|
+
GraspError,
|
|
209
|
+
AuthenticationError,
|
|
210
|
+
NotFoundError,
|
|
211
|
+
RateLimitError
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
container = client.create()
|
|
216
|
+
except AuthenticationError:
|
|
217
|
+
print("Invalid API key")
|
|
218
|
+
except RateLimitError as e:
|
|
219
|
+
print(f"Rate limited. Retry after: {e.retry_after}")
|
|
220
|
+
except GraspError as e:
|
|
221
|
+
print(f"API error: {e}")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Exception Hierarchy
|
|
225
|
+
|
|
226
|
+
- `GraspError`: Base exception
|
|
227
|
+
- `APIError`: API-related errors
|
|
228
|
+
- `APIStatusError`: HTTP status errors
|
|
229
|
+
- `AuthenticationError`: 401 Unauthorized
|
|
230
|
+
- `NotFoundError`: 404 Not Found
|
|
231
|
+
- `RateLimitError`: 429 Rate Limited
|
|
232
|
+
- `InternalServerError`: 500 Server Error
|
|
233
|
+
- `APIConnectionError`: Network issues
|
|
234
|
+
- `APITimeoutError`: Request timeout
|
|
235
|
+
|
|
236
|
+
## Examples
|
|
237
|
+
|
|
238
|
+
See the [examples](examples/) directory for more detailed examples:
|
|
239
|
+
|
|
240
|
+
- [basic_usage.py](examples/basic_usage.py): Simple synchronous usage
|
|
241
|
+
- [async_usage.py](examples/async_usage.py): Async with multiple containers
|
|
242
|
+
- [playwright_integration.py](examples/playwright_integration.py): Full Playwright integration
|
|
243
|
+
|
|
244
|
+
## Development
|
|
245
|
+
|
|
246
|
+
### Setup Development Environment
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Clone the repository
|
|
250
|
+
git clone https://github.com/getgrasp-ai/grasp.git
|
|
251
|
+
cd grasp/packages/python-sdk
|
|
252
|
+
|
|
253
|
+
# Install dependencies
|
|
254
|
+
pip install -e ".[dev]"
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Run Tests
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Run all tests
|
|
261
|
+
pytest
|
|
262
|
+
|
|
263
|
+
# Run with coverage
|
|
264
|
+
pytest --cov=grasp
|
|
265
|
+
|
|
266
|
+
# Run specific test file
|
|
267
|
+
pytest tests/test_client.py
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Code Quality
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
# Format code
|
|
274
|
+
black src/ tests/
|
|
275
|
+
|
|
276
|
+
# Lint
|
|
277
|
+
ruff check src/ tests/
|
|
278
|
+
|
|
279
|
+
# Type check
|
|
280
|
+
mypy src/
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Requirements
|
|
284
|
+
|
|
285
|
+
- Python 3.8+
|
|
286
|
+
- httpx >= 0.24.0
|
|
287
|
+
- pydantic >= 2.0
|
|
288
|
+
|
|
289
|
+
## License
|
|
290
|
+
|
|
291
|
+
MIT
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Grasp Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the Grasp browser automation platform.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Dual Client Architecture**: Both synchronous and asynchronous clients
|
|
8
|
+
- **Type Safety**: Full type hints and Pydantic models for validation
|
|
9
|
+
- **Browser Automation**: Seamless integration with Playwright and Puppeteer
|
|
10
|
+
- **Proxy Support**: Built-in proxy configuration for different use cases
|
|
11
|
+
- **Error Handling**: Comprehensive exception hierarchy
|
|
12
|
+
- **Auto-retry**: Automatic retry with exponential backoff
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install grasp-sdk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### Synchronous Usage
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from grasp import Grasp
|
|
26
|
+
|
|
27
|
+
# Initialize client
|
|
28
|
+
client = Grasp(api_key="your-api-key") # or set GRASP_API_KEY env var
|
|
29
|
+
|
|
30
|
+
# Create a container
|
|
31
|
+
container = client.create(
|
|
32
|
+
idle_timeout=30000, # 30 seconds
|
|
33
|
+
proxy={
|
|
34
|
+
"enabled": True,
|
|
35
|
+
"type": "residential",
|
|
36
|
+
"country": "US"
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Access browser information
|
|
41
|
+
print(f"Container ID: {container.id}")
|
|
42
|
+
print(f"CDP WebSocket: {container.browser.ws_endpoint}")
|
|
43
|
+
print(f"Live View: {container.browser.live_url}")
|
|
44
|
+
|
|
45
|
+
# Shutdown when done
|
|
46
|
+
container.shutdown()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Asynchronous Usage
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
import asyncio
|
|
53
|
+
from grasp import AsyncGrasp
|
|
54
|
+
|
|
55
|
+
async def main():
|
|
56
|
+
# Use context manager for automatic cleanup
|
|
57
|
+
async with AsyncGrasp(api_key="your-api-key") as client:
|
|
58
|
+
# Create container
|
|
59
|
+
container = await client.create(idle_timeout=30000)
|
|
60
|
+
|
|
61
|
+
# Use the container
|
|
62
|
+
print(f"Container ID: {container.id}")
|
|
63
|
+
|
|
64
|
+
# Shutdown
|
|
65
|
+
await container.shutdown()
|
|
66
|
+
|
|
67
|
+
asyncio.run(main())
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Playwright Integration
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from grasp import Grasp
|
|
74
|
+
from playwright.sync_api import sync_playwright
|
|
75
|
+
|
|
76
|
+
# Create Grasp container
|
|
77
|
+
grasp = Grasp()
|
|
78
|
+
container = grasp.create()
|
|
79
|
+
|
|
80
|
+
# Connect Playwright
|
|
81
|
+
with sync_playwright() as p:
|
|
82
|
+
browser = p.chromium.connect_over_cdp(container.browser.ws_endpoint)
|
|
83
|
+
page = browser.new_page()
|
|
84
|
+
page.goto("https://example.com")
|
|
85
|
+
|
|
86
|
+
# Your automation code here
|
|
87
|
+
title = page.title()
|
|
88
|
+
print(f"Page title: {title}")
|
|
89
|
+
|
|
90
|
+
browser.close()
|
|
91
|
+
|
|
92
|
+
# Cleanup
|
|
93
|
+
container.shutdown()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Configuration
|
|
97
|
+
|
|
98
|
+
### Environment Variables
|
|
99
|
+
|
|
100
|
+
- `GRASP_API_KEY`: Your API key (alternative to passing in code)
|
|
101
|
+
- `GRASP_BASE_URL`: API base URL (defaults to `https://api.getgrasp.ai`)
|
|
102
|
+
|
|
103
|
+
### Client Options
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
client = Grasp(
|
|
107
|
+
api_key="your-api-key",
|
|
108
|
+
base_url="https://api.getgrasp.ai", # optional
|
|
109
|
+
timeout=60.0, # request timeout in seconds
|
|
110
|
+
max_retries=2 # maximum retry attempts
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Container Options
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
container = client.create(
|
|
118
|
+
idle_timeout=30000, # milliseconds before container sleeps
|
|
119
|
+
proxy={
|
|
120
|
+
"enabled": True,
|
|
121
|
+
"type": "residential", # mobile, residential, isp, datacenter, custom
|
|
122
|
+
"country": "US", # optional: country code
|
|
123
|
+
"state": "CA", # optional: state code
|
|
124
|
+
"city": "LA" # optional: city name
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## API Reference
|
|
130
|
+
|
|
131
|
+
### Main Classes
|
|
132
|
+
|
|
133
|
+
#### `Grasp` / `AsyncGrasp`
|
|
134
|
+
Main client classes for interacting with the API.
|
|
135
|
+
|
|
136
|
+
**Methods:**
|
|
137
|
+
- `create(**options)`: Create a new container
|
|
138
|
+
- `connect(container_id)`: Connect to existing container
|
|
139
|
+
|
|
140
|
+
#### `GraspContainer` / `AsyncGraspContainer`
|
|
141
|
+
Represents a browser automation container.
|
|
142
|
+
|
|
143
|
+
**Properties:**
|
|
144
|
+
- `id`: Container identifier
|
|
145
|
+
- `status`: Current status (running, stopped, sleeping)
|
|
146
|
+
- `created_at`: Creation timestamp
|
|
147
|
+
- `browser`: Browser session information
|
|
148
|
+
|
|
149
|
+
**Methods:**
|
|
150
|
+
- `shutdown()`: Shut down the container
|
|
151
|
+
|
|
152
|
+
#### `BrowserSession`
|
|
153
|
+
Browser session information.
|
|
154
|
+
|
|
155
|
+
**Properties:**
|
|
156
|
+
- `ws_endpoint`: Chrome DevTools Protocol WebSocket endpoint
|
|
157
|
+
- `live_url`: Live view URL for observing the browser
|
|
158
|
+
|
|
159
|
+
### Exception Handling
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from grasp import (
|
|
163
|
+
GraspError,
|
|
164
|
+
AuthenticationError,
|
|
165
|
+
NotFoundError,
|
|
166
|
+
RateLimitError
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
container = client.create()
|
|
171
|
+
except AuthenticationError:
|
|
172
|
+
print("Invalid API key")
|
|
173
|
+
except RateLimitError as e:
|
|
174
|
+
print(f"Rate limited. Retry after: {e.retry_after}")
|
|
175
|
+
except GraspError as e:
|
|
176
|
+
print(f"API error: {e}")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Exception Hierarchy
|
|
180
|
+
|
|
181
|
+
- `GraspError`: Base exception
|
|
182
|
+
- `APIError`: API-related errors
|
|
183
|
+
- `APIStatusError`: HTTP status errors
|
|
184
|
+
- `AuthenticationError`: 401 Unauthorized
|
|
185
|
+
- `NotFoundError`: 404 Not Found
|
|
186
|
+
- `RateLimitError`: 429 Rate Limited
|
|
187
|
+
- `InternalServerError`: 500 Server Error
|
|
188
|
+
- `APIConnectionError`: Network issues
|
|
189
|
+
- `APITimeoutError`: Request timeout
|
|
190
|
+
|
|
191
|
+
## Examples
|
|
192
|
+
|
|
193
|
+
See the [examples](examples/) directory for more detailed examples:
|
|
194
|
+
|
|
195
|
+
- [basic_usage.py](examples/basic_usage.py): Simple synchronous usage
|
|
196
|
+
- [async_usage.py](examples/async_usage.py): Async with multiple containers
|
|
197
|
+
- [playwright_integration.py](examples/playwright_integration.py): Full Playwright integration
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
### Setup Development Environment
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Clone the repository
|
|
205
|
+
git clone https://github.com/getgrasp-ai/grasp.git
|
|
206
|
+
cd grasp/packages/python-sdk
|
|
207
|
+
|
|
208
|
+
# Install dependencies
|
|
209
|
+
pip install -e ".[dev]"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Run Tests
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# Run all tests
|
|
216
|
+
pytest
|
|
217
|
+
|
|
218
|
+
# Run with coverage
|
|
219
|
+
pytest --cov=grasp
|
|
220
|
+
|
|
221
|
+
# Run specific test file
|
|
222
|
+
pytest tests/test_client.py
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Code Quality
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# Format code
|
|
229
|
+
black src/ tests/
|
|
230
|
+
|
|
231
|
+
# Lint
|
|
232
|
+
ruff check src/ tests/
|
|
233
|
+
|
|
234
|
+
# Type check
|
|
235
|
+
mypy src/
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Requirements
|
|
239
|
+
|
|
240
|
+
- Python 3.8+
|
|
241
|
+
- httpx >= 0.24.0
|
|
242
|
+
- pydantic >= 2.0
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
MIT
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Release Guide for Grasp Python SDK
|
|
2
|
+
|
|
3
|
+
## Pre-release Checklist
|
|
4
|
+
|
|
5
|
+
- [ ] All tests pass locally
|
|
6
|
+
- [ ] Version number updated in `pyproject.toml`
|
|
7
|
+
- [ ] README.md is up to date
|
|
8
|
+
- [ ] CHANGELOG updated (if exists)
|
|
9
|
+
- [ ] Examples work correctly
|
|
10
|
+
|
|
11
|
+
## Current Release Info
|
|
12
|
+
|
|
13
|
+
- **Package Name**: grasp-sdk (on PyPI)
|
|
14
|
+
- **Import Name**: grasp (in Python code)
|
|
15
|
+
- **Previous Version**: 0.1.11 (from previous maintainer)
|
|
16
|
+
- **New Version**: 0.2.0 (complete rewrite)
|
|
17
|
+
|
|
18
|
+
## Publishing Steps
|
|
19
|
+
|
|
20
|
+
### 1. Clean previous builds
|
|
21
|
+
```bash
|
|
22
|
+
rm -rf dist/ build/ *.egg-info/
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 2. Install build tools
|
|
26
|
+
```bash
|
|
27
|
+
pip install --upgrade build twine
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 3. Build the package
|
|
31
|
+
```bash
|
|
32
|
+
python -m build
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 4. Check the distribution
|
|
36
|
+
```bash
|
|
37
|
+
# Check if package is correctly structured
|
|
38
|
+
twine check dist/*
|
|
39
|
+
|
|
40
|
+
# Test installation locally
|
|
41
|
+
pip install dist/grasp_sdk-0.2.0-py3-none-any.whl
|
|
42
|
+
|
|
43
|
+
# Verify it works
|
|
44
|
+
python -c "from grasp import Grasp; print('Import successful!')"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 5. Upload to Test PyPI (optional but recommended)
|
|
48
|
+
```bash
|
|
49
|
+
# Upload to test.pypi.org first
|
|
50
|
+
twine upload --repository testpypi dist/*
|
|
51
|
+
|
|
52
|
+
# Test install from Test PyPI
|
|
53
|
+
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ grasp-sdk==0.2.0
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 6. Upload to PyPI
|
|
57
|
+
```bash
|
|
58
|
+
# You'll need PyPI credentials or token
|
|
59
|
+
twine upload dist/*
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Authentication
|
|
63
|
+
|
|
64
|
+
You'll need PyPI credentials. There are two options:
|
|
65
|
+
|
|
66
|
+
### Option 1: Using Token (Recommended)
|
|
67
|
+
1. Go to https://pypi.org/manage/account/token/
|
|
68
|
+
2. Create a token for `grasp-sdk` project
|
|
69
|
+
3. Use `__token__` as username and the token as password
|
|
70
|
+
|
|
71
|
+
### Option 2: Using .pypirc file
|
|
72
|
+
Create `~/.pypirc`:
|
|
73
|
+
```ini
|
|
74
|
+
[distutils]
|
|
75
|
+
index-servers =
|
|
76
|
+
pypi
|
|
77
|
+
testpypi
|
|
78
|
+
|
|
79
|
+
[pypi]
|
|
80
|
+
username = __token__
|
|
81
|
+
password = pypi-YOUR-TOKEN-HERE
|
|
82
|
+
|
|
83
|
+
[testpypi]
|
|
84
|
+
repository = https://test.pypi.org/legacy/
|
|
85
|
+
username = __token__
|
|
86
|
+
password = pypi-YOUR-TEST-TOKEN-HERE
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Post-release
|
|
90
|
+
|
|
91
|
+
After successful release:
|
|
92
|
+
1. Create a git tag: `git tag v0.2.0`
|
|
93
|
+
2. Push tag: `git push origin v0.2.0`
|
|
94
|
+
3. Create GitHub release with changelog
|
|
95
|
+
4. Update documentation if needed
|
|
96
|
+
|
|
97
|
+
## Breaking Changes from 0.1.11
|
|
98
|
+
|
|
99
|
+
Since this is a complete rewrite, users upgrading from 0.1.11 should be aware:
|
|
100
|
+
- Completely new API design
|
|
101
|
+
- Both sync and async clients available
|
|
102
|
+
- Different import structure
|
|
103
|
+
- New error handling system
|
|
104
|
+
- Better container lifecycle management
|
|
105
|
+
|
|
106
|
+
## Version History
|
|
107
|
+
|
|
108
|
+
- **0.1.11** - Last version from previous maintainer
|
|
109
|
+
- **0.2.0** - Complete rewrite with new architecture
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Testing the Grasp Python SDK
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
1. **API Server Running**: Ensure the Grasp API server is running locally on `http://localhost:3000`
|
|
6
|
+
```bash
|
|
7
|
+
# From the root directory
|
|
8
|
+
pnpm --filter api-server dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
2. **Python Environment**: Python 3.8+ installed
|
|
12
|
+
|
|
13
|
+
## Running Tests
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd packages/python-sdk
|
|
17
|
+
./run_tests.sh
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The test script will automatically:
|
|
21
|
+
- Set environment variables (`GRASP_API_KEY="api-server-test"`, `GRASP_BASE_URL="http://localhost:3000"`)
|
|
22
|
+
- Install the SDK in development mode
|
|
23
|
+
- Install test dependencies (pytest, pytest-asyncio)
|
|
24
|
+
- Install Playwright for browser automation tests
|
|
25
|
+
- Run all tests including Playwright integration tests
|
|
26
|
+
- Show clear pass/fail status with troubleshooting tips
|
|
27
|
+
|
|
28
|
+
## Test Structure
|
|
29
|
+
|
|
30
|
+
- `tests/test_client.py` - Tests for synchronous client
|
|
31
|
+
- `tests/test_async_client.py` - Tests for asynchronous client
|
|
32
|
+
|
|
33
|
+
Each test file covers:
|
|
34
|
+
- Client initialization
|
|
35
|
+
- Container creation and management
|
|
36
|
+
- Browser endpoint validation
|
|
37
|
+
- Error handling
|
|
38
|
+
- Serialization
|
|
39
|
+
- **Playwright Integration** (optional):
|
|
40
|
+
- Browser connection via CDP
|
|
41
|
+
- Page navigation and content scraping
|
|
42
|
+
|
|
43
|
+
## Troubleshooting
|
|
44
|
+
|
|
45
|
+
### Connection Errors
|
|
46
|
+
If you see connection errors, ensure:
|
|
47
|
+
1. API server is running on port 3000
|
|
48
|
+
2. Environment variables are set correctly
|
|
49
|
+
3. No firewall blocking localhost connections
|
|
50
|
+
|
|
51
|
+
### API Key Errors
|
|
52
|
+
The test API key `api-server-test` is hardcoded for local testing. Make sure the API server recognizes this key.
|
|
53
|
+
|
|
54
|
+
### Cleanup
|
|
55
|
+
Tests automatically clean up containers after completion. If tests fail, some containers might remain active. Check the API server logs for orphaned containers.
|