fleet-python 0.2.71__tar.gz → 0.2.72b2__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.
- {fleet_python-0.2.71/fleet_python.egg-info → fleet_python-0.2.72b2}/PKG-INFO +1 -1
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/client.py +163 -6
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/instance/client.py +19 -4
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/resources/sqlite.py +633 -38
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/tasks.py +5 -2
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/client.py +183 -15
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/instance/client.py +20 -5
- fleet_python-0.2.72b2/fleet/resources/sqlite.py +1350 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/tasks.py +5 -2
- {fleet_python-0.2.71 → fleet_python-0.2.72b2/fleet_python.egg-info}/PKG-INFO +1 -1
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet_python.egg-info/SOURCES.txt +4 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/pyproject.toml +2 -1
- fleet_python-0.2.72b2/tests/test_app_method.py +85 -0
- fleet_python-0.2.72b2/tests/test_instance_dispatch.py +607 -0
- fleet_python-0.2.72b2/tests/test_sqlite_resource_dual_mode.py +263 -0
- fleet_python-0.2.72b2/tests/test_sqlite_shared_memory_behavior.py +117 -0
- fleet_python-0.2.71/fleet/resources/sqlite.py +0 -735
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/LICENSE +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/README.md +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/diff_example.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/dsl_example.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/example.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/exampleResume.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/example_account.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/example_action_log.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/example_client.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/example_mcp_anthropic.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/example_mcp_openai.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/example_sync.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/example_task.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/example_tasks.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/example_verifier.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/export_tasks.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/gemini_example.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/import_tasks.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/json_tasks_example.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/nova_act_example.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/openai_example.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/openai_simple_example.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/query_builder_example.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/quickstart.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/examples/test_cdp_logging.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/base.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/env/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/env/client.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/exceptions.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/global_client.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/instance/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/instance/base.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/models.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/resources/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/resources/base.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/resources/browser.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/resources/mcp.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/verifiers/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/verifiers/bundler.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/_async/verifiers/verifier.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/base.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/config.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/env/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/env/client.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/exceptions.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/global_client.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/instance/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/instance/base.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/instance/models.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/models.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/resources/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/resources/base.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/resources/browser.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/resources/mcp.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/types.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/verifiers/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/verifiers/bundler.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/verifiers/code.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/verifiers/db.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/verifiers/decorator.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/verifiers/parse.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/verifiers/sql_differ.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet/verifiers/verifier.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet_python.egg-info/dependency_links.txt +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet_python.egg-info/requires.txt +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/fleet_python.egg-info/top_level.txt +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/scripts/fix_sync_imports.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/scripts/unasync.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/setup.cfg +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/tests/__init__.py +0 -0
- {fleet_python-0.2.71 → fleet_python-0.2.72b2}/tests/test_verifier_from_string.py +0 -0
|
@@ -21,7 +21,7 @@ import httpx
|
|
|
21
21
|
import json
|
|
22
22
|
import logging
|
|
23
23
|
import os
|
|
24
|
-
from typing import List, Optional, Dict, Any, TYPE_CHECKING
|
|
24
|
+
from typing import List, Optional, Dict, Any, TYPE_CHECKING, Union
|
|
25
25
|
|
|
26
26
|
from .base import EnvironmentBase, AsyncWrapper
|
|
27
27
|
from ..models import (
|
|
@@ -49,6 +49,11 @@ from .instance import (
|
|
|
49
49
|
ResetResponse,
|
|
50
50
|
ExecuteFunctionResponse,
|
|
51
51
|
)
|
|
52
|
+
from ..instance.models import (
|
|
53
|
+
Resource as ResourceModel,
|
|
54
|
+
ResourceType,
|
|
55
|
+
ResourceMode,
|
|
56
|
+
)
|
|
52
57
|
from ..config import (
|
|
53
58
|
DEFAULT_MAX_RETRIES,
|
|
54
59
|
DEFAULT_TIMEOUT,
|
|
@@ -310,11 +315,163 @@ class AsyncFleet:
|
|
|
310
315
|
for instance_data in response.json()
|
|
311
316
|
]
|
|
312
317
|
|
|
313
|
-
async def instance(self, instance_id: str) -> AsyncEnv:
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
+
async def instance(self, instance_id: Union[str, Dict[str, str]]) -> AsyncEnv:
|
|
319
|
+
"""Create or connect to an environment instance.
|
|
320
|
+
|
|
321
|
+
Supports three modes based on input type:
|
|
322
|
+
1. dict: Local filesystem mode - {"current": "./data.db", "seed": "./seed.db"}
|
|
323
|
+
2. str starting with http:// or https://: Localhost/URL mode
|
|
324
|
+
3. str (other): Remote cloud instance mode
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
instance_id: Instance identifier (str), URL (str starting with http://),
|
|
328
|
+
or local db mapping (dict)
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
AsyncEnv: Environment instance
|
|
332
|
+
"""
|
|
333
|
+
# Local filesystem mode - dict of resource names to file paths
|
|
334
|
+
if isinstance(instance_id, dict):
|
|
335
|
+
return self._create_local_instance(instance_id)
|
|
336
|
+
|
|
337
|
+
# Localhost/direct URL mode - string starting with http:// or https://
|
|
338
|
+
elif isinstance(instance_id, str) and instance_id.startswith(("http://", "https://")):
|
|
339
|
+
return self._create_url_instance(instance_id)
|
|
340
|
+
|
|
341
|
+
# Remote mode - existing behavior
|
|
342
|
+
else:
|
|
343
|
+
response = await self.client.request("GET", f"/v1/env/instances/{instance_id}")
|
|
344
|
+
instance = AsyncEnv(client=self.client, **response.json())
|
|
345
|
+
await instance.instance.load()
|
|
346
|
+
return instance
|
|
347
|
+
|
|
348
|
+
def _create_url_instance(self, base_url: str) -> AsyncEnv:
|
|
349
|
+
"""Create instance connected to a direct URL (localhost or custom).
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
base_url: URL of the instance manager API
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
AsyncEnv: Environment instance configured for URL mode
|
|
356
|
+
"""
|
|
357
|
+
instance_client = AsyncInstanceClient(url=base_url, httpx_client=self._httpx_client)
|
|
358
|
+
|
|
359
|
+
# Create a minimal environment for URL mode
|
|
360
|
+
env = AsyncEnv(
|
|
361
|
+
client=self.client,
|
|
362
|
+
instance_id=base_url,
|
|
363
|
+
env_key="localhost",
|
|
364
|
+
version="",
|
|
365
|
+
status="running",
|
|
366
|
+
subdomain="localhost",
|
|
367
|
+
created_at="",
|
|
368
|
+
updated_at="",
|
|
369
|
+
terminated_at=None,
|
|
370
|
+
team_id="",
|
|
371
|
+
region="localhost",
|
|
372
|
+
env_variables=None,
|
|
373
|
+
data_key=None,
|
|
374
|
+
data_version=None,
|
|
375
|
+
urls=None,
|
|
376
|
+
health=None,
|
|
377
|
+
)
|
|
378
|
+
env._instance = instance_client
|
|
379
|
+
return env
|
|
380
|
+
|
|
381
|
+
@staticmethod
|
|
382
|
+
def _normalize_db_path(path: str) -> tuple[str, bool]:
|
|
383
|
+
"""Normalize database path and detect if it's in-memory.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
path: Database path - can be:
|
|
387
|
+
- File path: "./data.db"
|
|
388
|
+
- Plain memory: ":memory:"
|
|
389
|
+
- Named memory: ":memory:namespace"
|
|
390
|
+
- URI: "file:name?mode=memory&cache=shared"
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
Tuple of (normalized_path, is_memory)
|
|
394
|
+
"""
|
|
395
|
+
import uuid
|
|
396
|
+
import sqlite3
|
|
397
|
+
|
|
398
|
+
if path == ":memory:":
|
|
399
|
+
# Plain :memory: - create unique namespace
|
|
400
|
+
name = f"mem_{uuid.uuid4().hex[:8]}"
|
|
401
|
+
return f"file:{name}?mode=memory&cache=shared", True
|
|
402
|
+
elif path.startswith(":memory:"):
|
|
403
|
+
# Named memory: :memory:current -> file:current?mode=memory&cache=shared
|
|
404
|
+
namespace = path[8:] # Remove ":memory:" prefix
|
|
405
|
+
return f"file:{namespace}?mode=memory&cache=shared", True
|
|
406
|
+
elif "mode=memory" in path:
|
|
407
|
+
# Already a proper memory URI
|
|
408
|
+
return path, True
|
|
409
|
+
else:
|
|
410
|
+
# Regular file path
|
|
411
|
+
return path, False
|
|
412
|
+
|
|
413
|
+
def _create_local_instance(self, dbs: Dict[str, str]) -> AsyncEnv:
|
|
414
|
+
"""Create instance with local file-based or in-memory SQLite resources.
|
|
415
|
+
|
|
416
|
+
Args:
|
|
417
|
+
dbs: Map of resource names to paths (e.g., {"current": "./data.db"} or
|
|
418
|
+
{"current": ":memory:current"})
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
AsyncEnv: Environment instance configured for local mode
|
|
422
|
+
"""
|
|
423
|
+
import sqlite3
|
|
424
|
+
|
|
425
|
+
instance_client = AsyncInstanceClient(url="local://", httpx_client=None)
|
|
426
|
+
instance_client._resources = [] # Mark as loaded
|
|
427
|
+
instance_client._memory_anchors = {} # Store anchor connections for in-memory DBs
|
|
428
|
+
|
|
429
|
+
# Store creation parameters for local AsyncSQLiteResources
|
|
430
|
+
# This allows db() to create new instances each time (matching HTTP mode behavior)
|
|
431
|
+
for name, path in dbs.items():
|
|
432
|
+
# Normalize path and detect if it's in-memory
|
|
433
|
+
normalized_path, is_memory = self._normalize_db_path(path)
|
|
434
|
+
|
|
435
|
+
# Create anchor connection for in-memory databases
|
|
436
|
+
# This keeps the database alive as long as the env exists
|
|
437
|
+
if is_memory:
|
|
438
|
+
anchor_conn = sqlite3.connect(normalized_path, uri=True)
|
|
439
|
+
instance_client._memory_anchors[name] = anchor_conn
|
|
440
|
+
|
|
441
|
+
resource_model = ResourceModel(
|
|
442
|
+
name=name,
|
|
443
|
+
type=ResourceType.db,
|
|
444
|
+
mode=ResourceMode.rw,
|
|
445
|
+
label=f"Local: {path}",
|
|
446
|
+
)
|
|
447
|
+
instance_client._resources_state[ResourceType.db.value][name] = {
|
|
448
|
+
'type': 'local',
|
|
449
|
+
'resource_model': resource_model,
|
|
450
|
+
'db_path': normalized_path,
|
|
451
|
+
'is_memory': is_memory
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
# Create a minimal environment for local mode
|
|
455
|
+
env = AsyncEnv(
|
|
456
|
+
client=self.client,
|
|
457
|
+
instance_id="local",
|
|
458
|
+
env_key="local",
|
|
459
|
+
version="",
|
|
460
|
+
status="running",
|
|
461
|
+
subdomain="local",
|
|
462
|
+
created_at="",
|
|
463
|
+
updated_at="",
|
|
464
|
+
terminated_at=None,
|
|
465
|
+
team_id="",
|
|
466
|
+
region="local",
|
|
467
|
+
env_variables=None,
|
|
468
|
+
data_key=None,
|
|
469
|
+
data_version=None,
|
|
470
|
+
urls=None,
|
|
471
|
+
health=None,
|
|
472
|
+
)
|
|
473
|
+
env._instance = instance_client
|
|
474
|
+
return env
|
|
318
475
|
|
|
319
476
|
async def check_bundle_exists(self, bundle_hash: str) -> VerifiersCheckResponse:
|
|
320
477
|
return await _check_bundle_exists(self.client, bundle_hash)
|
|
@@ -85,9 +85,17 @@ class AsyncInstanceClient:
|
|
|
85
85
|
Returns:
|
|
86
86
|
An SQLite database resource for the given database name
|
|
87
87
|
"""
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
88
|
+
resource_info = self._resources_state[ResourceType.db.value][name]
|
|
89
|
+
# Local mode - resource_info is a dict with creation parameters
|
|
90
|
+
if isinstance(resource_info, dict) and resource_info.get('type') == 'local':
|
|
91
|
+
# Create new instance each time (matching HTTP mode behavior)
|
|
92
|
+
return AsyncSQLiteResource(
|
|
93
|
+
resource_info['resource_model'],
|
|
94
|
+
client=None,
|
|
95
|
+
db_path=resource_info['db_path']
|
|
96
|
+
)
|
|
97
|
+
# HTTP mode - resource_info is a ResourceModel, create new wrapper
|
|
98
|
+
return AsyncSQLiteResource(resource_info, self.client)
|
|
91
99
|
|
|
92
100
|
def browser(self, name: str) -> AsyncBrowserResource:
|
|
93
101
|
return AsyncBrowserResource(
|
|
@@ -177,10 +185,17 @@ class AsyncInstanceClient:
|
|
|
177
185
|
response = await self.client.request("GET", "/health")
|
|
178
186
|
return HealthResponse(**response.json())
|
|
179
187
|
|
|
188
|
+
def close(self):
|
|
189
|
+
"""Close anchor connections for in-memory databases."""
|
|
190
|
+
if hasattr(self, '_memory_anchors'):
|
|
191
|
+
for conn in self._memory_anchors.values():
|
|
192
|
+
conn.close()
|
|
193
|
+
self._memory_anchors.clear()
|
|
194
|
+
|
|
180
195
|
async def __aenter__(self):
|
|
181
196
|
"""Async context manager entry."""
|
|
182
197
|
return self
|
|
183
198
|
|
|
184
199
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
185
200
|
"""Async context manager exit."""
|
|
186
|
-
|
|
201
|
+
self.close()
|