fleet-python 0.2.60__py3-none-any.whl → 0.2.62__py3-none-any.whl
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 fleet-python might be problematic. Click here for more details.
- examples/export_tasks.py +19 -2
- fleet/_async/client.py +6 -0
- fleet/_async/models.py +4 -0
- fleet/_async/tasks.py +10 -2
- fleet/client.py +6 -0
- fleet/models.py +8 -0
- fleet/tasks.py +10 -2
- {fleet_python-0.2.60.dist-info → fleet_python-0.2.62.dist-info}/METADATA +1 -1
- {fleet_python-0.2.60.dist-info → fleet_python-0.2.62.dist-info}/RECORD +14 -12
- {fleet_python-0.2.60.dist-info → fleet_python-0.2.62.dist-info}/top_level.txt +1 -0
- tests/__init__.py +1 -0
- tests/test_verifier_from_string.py +420 -0
- {fleet_python-0.2.60.dist-info → fleet_python-0.2.62.dist-info}/WHEEL +0 -0
- {fleet_python-0.2.60.dist-info → fleet_python-0.2.62.dist-info}/licenses/LICENSE +0 -0
examples/export_tasks.py
CHANGED
|
@@ -21,6 +21,12 @@ def main():
|
|
|
21
21
|
help="Optional list of task keys to export (space-separated)",
|
|
22
22
|
default=None,
|
|
23
23
|
)
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"--task-project-key",
|
|
26
|
+
"-tpk",
|
|
27
|
+
help="Optional task project key to filter tasks",
|
|
28
|
+
default=None,
|
|
29
|
+
)
|
|
24
30
|
parser.add_argument(
|
|
25
31
|
"--output",
|
|
26
32
|
"-o",
|
|
@@ -31,9 +37,17 @@ def main():
|
|
|
31
37
|
args = parser.parse_args()
|
|
32
38
|
|
|
33
39
|
# Validate that only one filter is specified
|
|
34
|
-
|
|
40
|
+
filters_specified = sum(
|
|
41
|
+
[
|
|
42
|
+
args.project_key is not None,
|
|
43
|
+
args.task_keys is not None,
|
|
44
|
+
args.task_project_key is not None,
|
|
45
|
+
]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if filters_specified > 1:
|
|
35
49
|
parser.error(
|
|
36
|
-
"Cannot specify
|
|
50
|
+
"Cannot specify multiple filters. Use only one of --project-key, --task-keys, or --task-project-key."
|
|
37
51
|
)
|
|
38
52
|
|
|
39
53
|
# Get account info
|
|
@@ -49,6 +63,9 @@ def main():
|
|
|
49
63
|
f"Loading {len(args.task_keys)} specific task(s): {', '.join(args.task_keys)}"
|
|
50
64
|
)
|
|
51
65
|
tasks = fleet.load_tasks(keys=args.task_keys)
|
|
66
|
+
elif args.task_project_key:
|
|
67
|
+
print(f"Loading tasks from task project: {args.task_project_key}")
|
|
68
|
+
tasks = fleet.load_tasks(task_project_key=args.task_project_key)
|
|
52
69
|
else:
|
|
53
70
|
print("Loading all tasks")
|
|
54
71
|
tasks = fleet.load_tasks()
|
fleet/_async/client.py
CHANGED
|
@@ -397,6 +397,7 @@ class AsyncFleet:
|
|
|
397
397
|
verifier_id=verifier_id, # Set verifier_id so _rebuild_verifier works
|
|
398
398
|
verifier_sha=verifier_sha, # Set verifier_sha
|
|
399
399
|
metadata=task_json.get("metadata", {}), # Default empty metadata
|
|
400
|
+
output_json_schema=task_json.get("output_json_schema"), # JSON schema for output
|
|
400
401
|
)
|
|
401
402
|
return task
|
|
402
403
|
|
|
@@ -407,6 +408,7 @@ class AsyncFleet:
|
|
|
407
408
|
version: Optional[str] = None,
|
|
408
409
|
team_id: Optional[str] = None,
|
|
409
410
|
project_key: Optional[str] = None,
|
|
411
|
+
task_project_key: Optional[str] = None,
|
|
410
412
|
data_id: Optional[str] = None,
|
|
411
413
|
data_version: Optional[str] = None,
|
|
412
414
|
) -> List[Task]:
|
|
@@ -418,6 +420,7 @@ class AsyncFleet:
|
|
|
418
420
|
version: Optional version to filter tasks by (client-side filter)
|
|
419
421
|
team_id: Optional team_id to filter by (admin only)
|
|
420
422
|
project_key: Optional project key to filter tasks by
|
|
423
|
+
task_project_key: Optional task project key to filter tasks by
|
|
421
424
|
data_id: Optional data identifier to filter tasks by
|
|
422
425
|
data_version: Optional data version to filter tasks by
|
|
423
426
|
|
|
@@ -433,6 +436,8 @@ class AsyncFleet:
|
|
|
433
436
|
params["team_id"] = team_id
|
|
434
437
|
if project_key is not None:
|
|
435
438
|
params["project_key"] = project_key
|
|
439
|
+
if task_project_key is not None:
|
|
440
|
+
params["task_project_key"] = task_project_key
|
|
436
441
|
if data_id is not None:
|
|
437
442
|
params["data_id"] = data_id
|
|
438
443
|
if data_version is not None:
|
|
@@ -547,6 +552,7 @@ class AsyncFleet:
|
|
|
547
552
|
verifier_func=verifier_func, # Set verifier code
|
|
548
553
|
verifier=verifier, # Use created verifier or None
|
|
549
554
|
metadata={}, # Default empty metadata
|
|
555
|
+
output_json_schema=getattr(task_response, "output_json_schema", None), # Get output_json_schema if available
|
|
550
556
|
)
|
|
551
557
|
tasks.append(task)
|
|
552
558
|
|
fleet/_async/models.py
CHANGED
|
@@ -154,6 +154,7 @@ class TaskRequest(BaseModel):
|
|
|
154
154
|
verifier_id: Optional[str] = Field(None, title="Verifier Id")
|
|
155
155
|
version: Optional[str] = Field(None, title="Version")
|
|
156
156
|
env_variables: Optional[Dict[str, Any]] = Field(None, title="Env Variables")
|
|
157
|
+
output_json_schema: Optional[Dict[str, Any]] = Field(None, title="Output Json Schema")
|
|
157
158
|
|
|
158
159
|
|
|
159
160
|
class TaskUpdateRequest(BaseModel):
|
|
@@ -180,8 +181,11 @@ class TaskResponse(BaseModel):
|
|
|
180
181
|
verifier_id: Optional[str] = Field(None, title="Verifier Id")
|
|
181
182
|
verifier_func: Optional[str] = Field(None, title="Verifier Func")
|
|
182
183
|
version: Optional[str] = Field(None, title="Version")
|
|
184
|
+
data_id: Optional[str] = Field(None, title="Data Id")
|
|
185
|
+
data_version: Optional[str] = Field(None, title="Data Version")
|
|
183
186
|
env_variables: Optional[Dict[str, Any]] = Field(None, title="Env Variables")
|
|
184
187
|
verifier: Optional[VerifierData] = Field(None, title="Verifier")
|
|
188
|
+
output_json_schema: Optional[Dict[str, Any]] = Field(None, title="Output Json Schema")
|
|
185
189
|
|
|
186
190
|
|
|
187
191
|
class ValidationError(BaseModel):
|
fleet/_async/tasks.py
CHANGED
|
@@ -38,6 +38,9 @@ class Task(BaseModel):
|
|
|
38
38
|
metadata: Optional[Dict[str, Any]] = Field(
|
|
39
39
|
default_factory=dict, description="Additional task metadata"
|
|
40
40
|
)
|
|
41
|
+
output_json_schema: Optional[Dict[str, Any]] = Field(
|
|
42
|
+
None, description="JSON schema for expected output format"
|
|
43
|
+
)
|
|
41
44
|
|
|
42
45
|
@validator("key")
|
|
43
46
|
def validate_key_format(cls, v):
|
|
@@ -286,10 +289,12 @@ def verifier_from_string(
|
|
|
286
289
|
# Execute the verifier code in the namespace
|
|
287
290
|
exec(verifier_func, globals(), local_namespace)
|
|
288
291
|
|
|
289
|
-
# Find the function that was defined
|
|
292
|
+
# Find the function that was defined (not imported)
|
|
293
|
+
# Functions defined via exec have co_filename == '<string>'
|
|
294
|
+
# Imported functions have their actual module file path
|
|
290
295
|
func_obj = None
|
|
291
296
|
for name, obj in local_namespace.items():
|
|
292
|
-
if inspect.isfunction(obj):
|
|
297
|
+
if inspect.isfunction(obj) and obj.__code__.co_filename == "<string>":
|
|
293
298
|
func_obj = obj
|
|
294
299
|
break
|
|
295
300
|
|
|
@@ -329,6 +334,7 @@ async def load_tasks(
|
|
|
329
334
|
version: Optional[str] = None,
|
|
330
335
|
team_id: Optional[str] = None,
|
|
331
336
|
project_key: Optional[str] = None,
|
|
337
|
+
task_project_key: Optional[str] = None,
|
|
332
338
|
data_id: Optional[str] = None,
|
|
333
339
|
data_version: Optional[str] = None,
|
|
334
340
|
) -> List[Task]:
|
|
@@ -340,6 +346,7 @@ async def load_tasks(
|
|
|
340
346
|
version: Optional version to filter tasks by
|
|
341
347
|
team_id: Optional team_id to filter by (admin only)
|
|
342
348
|
project_key: Optional project key to filter tasks by
|
|
349
|
+
task_project_key: Optional task project key to filter tasks by
|
|
343
350
|
data_id: Optional data identifier to filter tasks by
|
|
344
351
|
data_version: Optional data version to filter tasks by
|
|
345
352
|
|
|
@@ -359,6 +366,7 @@ async def load_tasks(
|
|
|
359
366
|
version=version,
|
|
360
367
|
team_id=team_id,
|
|
361
368
|
project_key=project_key,
|
|
369
|
+
task_project_key=task_project_key,
|
|
362
370
|
data_id=data_id,
|
|
363
371
|
data_version=data_version,
|
|
364
372
|
)
|
fleet/client.py
CHANGED
|
@@ -395,6 +395,7 @@ class Fleet:
|
|
|
395
395
|
verifier_id=verifier_id, # Set verifier_id so _rebuild_verifier works
|
|
396
396
|
verifier_sha=verifier_sha, # Set verifier_sha
|
|
397
397
|
metadata=task_json.get("metadata", {}), # Default empty metadata
|
|
398
|
+
output_json_schema=task_json.get("output_json_schema"), # JSON schema for output
|
|
398
399
|
)
|
|
399
400
|
return task
|
|
400
401
|
|
|
@@ -405,6 +406,7 @@ class Fleet:
|
|
|
405
406
|
version: Optional[str] = None,
|
|
406
407
|
team_id: Optional[str] = None,
|
|
407
408
|
project_key: Optional[str] = None,
|
|
409
|
+
task_project_key: Optional[str] = None,
|
|
408
410
|
data_id: Optional[str] = None,
|
|
409
411
|
data_version: Optional[str] = None,
|
|
410
412
|
) -> List[Task]:
|
|
@@ -416,6 +418,7 @@ class Fleet:
|
|
|
416
418
|
version: Optional version to filter tasks by (client-side filter)
|
|
417
419
|
team_id: Optional team_id to filter by (admin only)
|
|
418
420
|
project_key: Optional project key to filter tasks by
|
|
421
|
+
task_project_key: Optional task project key to filter tasks by
|
|
419
422
|
data_id: Optional data identifier to filter tasks by
|
|
420
423
|
data_version: Optional data version to filter tasks by
|
|
421
424
|
|
|
@@ -431,6 +434,8 @@ class Fleet:
|
|
|
431
434
|
params["team_id"] = team_id
|
|
432
435
|
if project_key is not None:
|
|
433
436
|
params["project_key"] = project_key
|
|
437
|
+
if task_project_key is not None:
|
|
438
|
+
params["task_project_key"] = task_project_key
|
|
434
439
|
if data_id is not None:
|
|
435
440
|
params["data_id"] = data_id
|
|
436
441
|
if data_version is not None:
|
|
@@ -553,6 +558,7 @@ class Fleet:
|
|
|
553
558
|
verifier_func=verifier_func, # Set verifier code
|
|
554
559
|
verifier=verifier, # Use created verifier or None
|
|
555
560
|
metadata={}, # Default empty metadata
|
|
561
|
+
output_json_schema=getattr(task_response, "output_json_schema", None), # Get output_json_schema if available
|
|
556
562
|
)
|
|
557
563
|
tasks.append(task)
|
|
558
564
|
|
fleet/models.py
CHANGED
|
@@ -157,6 +157,9 @@ class TaskRequest(BaseModel):
|
|
|
157
157
|
verifier_id: Optional[str] = Field(None, title="Verifier Id")
|
|
158
158
|
version: Optional[str] = Field(None, title="Version")
|
|
159
159
|
env_variables: Optional[Dict[str, Any]] = Field(None, title="Env Variables")
|
|
160
|
+
output_json_schema: Optional[Dict[str, Any]] = Field(
|
|
161
|
+
None, title="Output Json Schema"
|
|
162
|
+
)
|
|
160
163
|
|
|
161
164
|
|
|
162
165
|
class TaskUpdateRequest(BaseModel):
|
|
@@ -183,8 +186,13 @@ class TaskResponse(BaseModel):
|
|
|
183
186
|
verifier_id: Optional[str] = Field(None, title="Verifier Id")
|
|
184
187
|
verifier_func: Optional[str] = Field(None, title="Verifier Func")
|
|
185
188
|
version: Optional[str] = Field(None, title="Version")
|
|
189
|
+
data_id: Optional[str] = Field(None, title="Data Id")
|
|
190
|
+
data_version: Optional[str] = Field(None, title="Data Version")
|
|
186
191
|
env_variables: Optional[Dict[str, Any]] = Field(None, title="Env Variables")
|
|
187
192
|
verifier: Optional[VerifierData] = Field(None, title="Verifier")
|
|
193
|
+
output_json_schema: Optional[Dict[str, Any]] = Field(
|
|
194
|
+
None, title="Output Json Schema"
|
|
195
|
+
)
|
|
188
196
|
|
|
189
197
|
|
|
190
198
|
class ValidationError(BaseModel):
|
fleet/tasks.py
CHANGED
|
@@ -39,6 +39,9 @@ class Task(BaseModel):
|
|
|
39
39
|
metadata: Optional[Dict[str, Any]] = Field(
|
|
40
40
|
default_factory=dict, description="Additional task metadata"
|
|
41
41
|
)
|
|
42
|
+
output_json_schema: Optional[Dict[str, Any]] = Field(
|
|
43
|
+
None, description="JSON schema for expected output format"
|
|
44
|
+
)
|
|
42
45
|
|
|
43
46
|
@validator("key")
|
|
44
47
|
def validate_key_format(cls, v):
|
|
@@ -283,10 +286,12 @@ def verifier_from_string(
|
|
|
283
286
|
# Execute the verifier code in the namespace
|
|
284
287
|
exec(verifier_func, exec_globals, local_namespace)
|
|
285
288
|
|
|
286
|
-
# Find the function that was defined
|
|
289
|
+
# Find the function that was defined (not imported)
|
|
290
|
+
# Functions defined via exec have co_filename == '<string>'
|
|
291
|
+
# Imported functions have their actual module file path
|
|
287
292
|
func_obj = None
|
|
288
293
|
for name, obj in local_namespace.items():
|
|
289
|
-
if inspect.isfunction(obj):
|
|
294
|
+
if inspect.isfunction(obj) and obj.__code__.co_filename == "<string>":
|
|
290
295
|
func_obj = obj
|
|
291
296
|
break
|
|
292
297
|
|
|
@@ -330,6 +335,7 @@ def load_tasks(
|
|
|
330
335
|
version: Optional[str] = None,
|
|
331
336
|
team_id: Optional[str] = None,
|
|
332
337
|
project_key: Optional[str] = None,
|
|
338
|
+
task_project_key: Optional[str] = None,
|
|
333
339
|
data_id: Optional[str] = None,
|
|
334
340
|
data_version: Optional[str] = None,
|
|
335
341
|
) -> List[Task]:
|
|
@@ -341,6 +347,7 @@ def load_tasks(
|
|
|
341
347
|
version: Optional version to filter tasks by
|
|
342
348
|
team_id: Optional team_id to filter by (admin only)
|
|
343
349
|
project_key: Optional project key to filter tasks by
|
|
350
|
+
task_project_key: Optional task project key to filter tasks by
|
|
344
351
|
data_id: Optional data identifier to filter tasks by
|
|
345
352
|
data_version: Optional data version to filter tasks by
|
|
346
353
|
|
|
@@ -360,6 +367,7 @@ def load_tasks(
|
|
|
360
367
|
version=version,
|
|
361
368
|
team_id=team_id,
|
|
362
369
|
project_key=project_key,
|
|
370
|
+
task_project_key=task_project_key,
|
|
363
371
|
data_id=data_id,
|
|
364
372
|
data_version=data_version,
|
|
365
373
|
)
|
|
@@ -11,7 +11,7 @@ examples/example_sync.py,sha256=EkuWmUzB1ZsBJQk6ZRflB793rKsuRHeSg5HJZHVhBB0,975
|
|
|
11
11
|
examples/example_task.py,sha256=dhG6STAkNsTdHs9cO1RFH9WfuvRmq5bRC211hTeFrk8,7088
|
|
12
12
|
examples/example_tasks.py,sha256=xTL8UWVAuolSX6swskfrAcmDrLIzn45dJ7YPWCwoEBU,514
|
|
13
13
|
examples/example_verifier.py,sha256=0vwNITIG3m4CkSPwIxNXcGx9TqrxEsCGqK2A8keKZMM,2392
|
|
14
|
-
examples/export_tasks.py,sha256=
|
|
14
|
+
examples/export_tasks.py,sha256=cJ_8xND7Q3IOM1JfJPR-DH3aLfHo_KmKJeO-1IVUFrQ,3237
|
|
15
15
|
examples/gemini_example.py,sha256=qj9WDazQTYNiRHNeUg9Tjkp33lJMwbx8gDfpFe1sDQo,16180
|
|
16
16
|
examples/import_tasks.py,sha256=pb60dOXnOn-G1INQxV1wujeHmmY2oWz9Ddj9SbMm1pk,3139
|
|
17
17
|
examples/json_tasks_example.py,sha256=CYPESGGtOo0fmsDdLidujTfsE4QlJHw7rOhyVqPJ_Ls,5329
|
|
@@ -23,20 +23,20 @@ examples/quickstart.py,sha256=1VT39IRRhemsJgxi0O0gprdpcw7HB4pYO97GAYagIcg,3788
|
|
|
23
23
|
examples/test_cdp_logging.py,sha256=AkCwQCgOTQEI8w3v0knWK_4eXMph7L9x07wj9yIYM10,2836
|
|
24
24
|
fleet/__init__.py,sha256=yC4HIcbtPAPOgI0lLri3l8nbXkNee9JOihKAc7SXYkY,4201
|
|
25
25
|
fleet/base.py,sha256=bc-340sTpq_DJs7yQ9d2pDWnmJFmA1SwDB9Lagvqtb4,9182
|
|
26
|
-
fleet/client.py,sha256
|
|
26
|
+
fleet/client.py,sha256=2zbX2a_cAfrpJMp48nfiIKPFCXE04ahtmogaymibtgg,32499
|
|
27
27
|
fleet/config.py,sha256=uY02ZKxVoXqVDta-0IMWaYJeE1CTXF_fA9NI6QUutmU,319
|
|
28
28
|
fleet/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
|
|
29
29
|
fleet/global_client.py,sha256=frrDAFNM2ywN0JHLtlm9qbE1dQpnQJsavJpb7xSR_bU,1072
|
|
30
|
-
fleet/models.py,sha256
|
|
31
|
-
fleet/tasks.py,sha256=
|
|
30
|
+
fleet/models.py,sha256=-8WG10SMkXUljRqD_8pfajCr4PS9qYDVLAZ8RxdJMb0,13392
|
|
31
|
+
fleet/tasks.py,sha256=STR34InS14rMq1DFPsCeAASarsvUqf_ODK_jitT8ORU,16914
|
|
32
32
|
fleet/types.py,sha256=L4Y82xICf1tzyCLqhLYUgEoaIIS5h9T05TyFNHSWs3s,652
|
|
33
33
|
fleet/_async/__init__.py,sha256=Wi8Tjj-Lfnxi4cPOkAxh2lynnpEBNni6mI6Iq80uOro,8054
|
|
34
34
|
fleet/_async/base.py,sha256=oisVTQsx0M_yTmyQJc3oij63uKZ97MHz-xYFsWXxQE8,9202
|
|
35
|
-
fleet/_async/client.py,sha256=
|
|
35
|
+
fleet/_async/client.py,sha256=Uu9o8LpUce8vOoFxqG0RDe4VtJpu1BJGIFbVecuO71g,32947
|
|
36
36
|
fleet/_async/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
|
|
37
37
|
fleet/_async/global_client.py,sha256=4WskpLHbsDEgWW7hXMD09W-brkp4euy8w2ZJ88594rQ,1103
|
|
38
|
-
fleet/_async/models.py,sha256=
|
|
39
|
-
fleet/_async/tasks.py,sha256=
|
|
38
|
+
fleet/_async/models.py,sha256=6x3IPYuWz1v6zWjujqgzK2CpR2HB5Rme4LQFLyvUDXE,13164
|
|
39
|
+
fleet/_async/tasks.py,sha256=LiDDIrmT9mIqlUna-I903n4yVpLxbmxUSv4lPdaBcDE,16942
|
|
40
40
|
fleet/_async/env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
41
|
fleet/_async/env/client.py,sha256=C5WG5Ir_McXaFPZNdkQjj0w4V7xMIcw3QyVP5g-3kVk,1237
|
|
42
42
|
fleet/_async/instance/__init__.py,sha256=PtmJq8J8bh0SOQ2V55QURz5GJfobozwtQoqhaOk3_tI,515
|
|
@@ -69,10 +69,12 @@ fleet/verifiers/decorator.py,sha256=nAP3O8szXu7md_kpwpz91hGSUNEVLYjwZQZTkQlV1DM,
|
|
|
69
69
|
fleet/verifiers/parse.py,sha256=qz9AfJrTbjlg-LU-lE8Ciqi7Yt2a8-cs17FdpjTLhMk,8550
|
|
70
70
|
fleet/verifiers/sql_differ.py,sha256=TqTLWyK3uOyLbitT6HYzYEzuSFC39wcyhgk3rcm__k8,6525
|
|
71
71
|
fleet/verifiers/verifier.py,sha256=_lcxXVm8e0xRrK2gNJy9up7pW1zOkPRY5n5lQ85S8jg,14197
|
|
72
|
-
fleet_python-0.2.
|
|
72
|
+
fleet_python-0.2.62.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
73
73
|
scripts/fix_sync_imports.py,sha256=X9fWLTpiPGkSHsjyQUDepOJkxOqw1DPj7nd8wFlFqLQ,8368
|
|
74
74
|
scripts/unasync.py,sha256=vWVQxRWX8SRZO5cmzEhpvnG_REhCWXpidIGIpWmEcvI,696
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
fleet_python-0.2.
|
|
78
|
-
fleet_python-0.2.
|
|
75
|
+
tests/__init__.py,sha256=Re1SdyxH8NfyL1kjhi7SQkGP1mYeWB-D6UALqdIMd8I,35
|
|
76
|
+
tests/test_verifier_from_string.py,sha256=Lxi3TpFHFb-hG4-UhLKZJkqo84ax9YJY8G6beO-1erM,13581
|
|
77
|
+
fleet_python-0.2.62.dist-info/METADATA,sha256=pkly98qTBJVHA9jkevvz_v3UKZyGa2-_RFVlbVGLlSg,3304
|
|
78
|
+
fleet_python-0.2.62.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
79
|
+
fleet_python-0.2.62.dist-info/top_level.txt,sha256=qb1zIbtEktyhRFZdqVytwg54l64qtoZL0wjHB4bUg3c,29
|
|
80
|
+
fleet_python-0.2.62.dist-info/RECORD,,
|
tests/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Tests package for fleet-sdk."""
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
"""Comprehensive tests for verifier_from_string function.
|
|
2
|
+
|
|
3
|
+
Tests both sync (fleet/tasks.py) and async (fleet/_async/tasks.py) versions.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from fleet.tasks import verifier_from_string as sync_verifier_from_string
|
|
8
|
+
from fleet._async.tasks import verifier_from_string as async_verifier_from_string
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestSyncVerifierFromString:
|
|
12
|
+
"""Tests for sync version of verifier_from_string."""
|
|
13
|
+
|
|
14
|
+
def test_basic_verifier_without_imports(self):
|
|
15
|
+
"""Test basic verifier function without any imports."""
|
|
16
|
+
code = """
|
|
17
|
+
def my_verifier(env):
|
|
18
|
+
return 1.0
|
|
19
|
+
"""
|
|
20
|
+
verifier = sync_verifier_from_string(
|
|
21
|
+
verifier_func=code,
|
|
22
|
+
verifier_id="test-verifier",
|
|
23
|
+
verifier_key="test-key",
|
|
24
|
+
sha256="test-sha",
|
|
25
|
+
)
|
|
26
|
+
assert verifier is not None
|
|
27
|
+
assert verifier.key == "test-key"
|
|
28
|
+
assert verifier.func.__name__ == "my_verifier"
|
|
29
|
+
|
|
30
|
+
def test_verifier_with_from_fleet_import_verifier(self):
|
|
31
|
+
"""Test the bug case: 'from fleet import verifier' should not be selected."""
|
|
32
|
+
code = """
|
|
33
|
+
from fleet import verifier
|
|
34
|
+
|
|
35
|
+
def my_actual_verifier(env):
|
|
36
|
+
return 1.0
|
|
37
|
+
"""
|
|
38
|
+
verifier = sync_verifier_from_string(
|
|
39
|
+
verifier_func=code,
|
|
40
|
+
verifier_id="test-verifier",
|
|
41
|
+
verifier_key="test-key",
|
|
42
|
+
sha256="test-sha",
|
|
43
|
+
)
|
|
44
|
+
assert verifier is not None
|
|
45
|
+
# The function name should be 'my_actual_verifier', not 'verifier'
|
|
46
|
+
assert verifier.func.__name__ == "my_actual_verifier"
|
|
47
|
+
|
|
48
|
+
def test_verifier_with_from_fleet_verifiers_import_verifier(self):
|
|
49
|
+
"""Test bug case: 'from fleet.verifiers import verifier'."""
|
|
50
|
+
code = """
|
|
51
|
+
from fleet.verifiers import verifier
|
|
52
|
+
|
|
53
|
+
def check_something(env):
|
|
54
|
+
return 1.0
|
|
55
|
+
"""
|
|
56
|
+
verifier = sync_verifier_from_string(
|
|
57
|
+
verifier_func=code,
|
|
58
|
+
verifier_id="test-verifier",
|
|
59
|
+
verifier_key="test-key",
|
|
60
|
+
sha256="test-sha",
|
|
61
|
+
)
|
|
62
|
+
assert verifier is not None
|
|
63
|
+
assert verifier.func.__name__ == "check_something"
|
|
64
|
+
|
|
65
|
+
def test_verifier_with_from_fleet_verifiers_verifier_import_verifier(self):
|
|
66
|
+
"""Test bug case: 'from fleet.verifiers.verifier import verifier'."""
|
|
67
|
+
code = """
|
|
68
|
+
from fleet.verifiers.verifier import verifier
|
|
69
|
+
|
|
70
|
+
def validate_task(env):
|
|
71
|
+
return 0.5
|
|
72
|
+
"""
|
|
73
|
+
verifier = sync_verifier_from_string(
|
|
74
|
+
verifier_func=code,
|
|
75
|
+
verifier_id="test-verifier",
|
|
76
|
+
verifier_key="test-key",
|
|
77
|
+
sha256="test-sha",
|
|
78
|
+
)
|
|
79
|
+
assert verifier is not None
|
|
80
|
+
assert verifier.func.__name__ == "validate_task"
|
|
81
|
+
|
|
82
|
+
def test_verifier_with_multiple_imports(self):
|
|
83
|
+
"""Test verifier with multiple import statements."""
|
|
84
|
+
code = """
|
|
85
|
+
from fleet import verifier
|
|
86
|
+
from fleet.verifiers.db import IgnoreConfig
|
|
87
|
+
import json
|
|
88
|
+
|
|
89
|
+
def complex_verifier(env):
|
|
90
|
+
data = json.dumps({"status": "ok"})
|
|
91
|
+
return 1.0
|
|
92
|
+
"""
|
|
93
|
+
verifier = sync_verifier_from_string(
|
|
94
|
+
verifier_func=code,
|
|
95
|
+
verifier_id="test-verifier",
|
|
96
|
+
verifier_key="test-key",
|
|
97
|
+
sha256="test-sha",
|
|
98
|
+
)
|
|
99
|
+
assert verifier is not None
|
|
100
|
+
assert verifier.func.__name__ == "complex_verifier"
|
|
101
|
+
|
|
102
|
+
def test_verifier_with_legitimate_imports(self):
|
|
103
|
+
"""Test verifier with legitimate imports (not fleet-related)."""
|
|
104
|
+
code = """
|
|
105
|
+
import json
|
|
106
|
+
import os
|
|
107
|
+
|
|
108
|
+
def my_verifier(env):
|
|
109
|
+
return 1.0
|
|
110
|
+
"""
|
|
111
|
+
verifier = sync_verifier_from_string(
|
|
112
|
+
verifier_func=code,
|
|
113
|
+
verifier_id="test-verifier",
|
|
114
|
+
verifier_key="test-key",
|
|
115
|
+
sha256="test-sha",
|
|
116
|
+
)
|
|
117
|
+
assert verifier is not None
|
|
118
|
+
assert verifier.func.__name__ == "my_verifier"
|
|
119
|
+
|
|
120
|
+
def test_verifier_using_task_scores(self):
|
|
121
|
+
"""Test verifier that uses TASK_SUCCESSFUL_SCORE and TASK_FAILED_SCORE."""
|
|
122
|
+
code = """
|
|
123
|
+
def my_verifier(env):
|
|
124
|
+
if True:
|
|
125
|
+
return TASK_SUCCESSFUL_SCORE
|
|
126
|
+
return TASK_FAILED_SCORE
|
|
127
|
+
"""
|
|
128
|
+
verifier = sync_verifier_from_string(
|
|
129
|
+
verifier_func=code,
|
|
130
|
+
verifier_id="test-verifier",
|
|
131
|
+
verifier_key="test-key",
|
|
132
|
+
sha256="test-sha",
|
|
133
|
+
)
|
|
134
|
+
assert verifier is not None
|
|
135
|
+
assert verifier.func.__name__ == "my_verifier"
|
|
136
|
+
|
|
137
|
+
def test_verifier_using_ignore_config(self):
|
|
138
|
+
"""Test verifier that uses IgnoreConfig."""
|
|
139
|
+
code = """
|
|
140
|
+
def my_verifier(env):
|
|
141
|
+
config = IgnoreConfig()
|
|
142
|
+
return 1.0
|
|
143
|
+
"""
|
|
144
|
+
verifier = sync_verifier_from_string(
|
|
145
|
+
verifier_func=code,
|
|
146
|
+
verifier_id="test-verifier",
|
|
147
|
+
verifier_key="test-key",
|
|
148
|
+
sha256="test-sha",
|
|
149
|
+
)
|
|
150
|
+
assert verifier is not None
|
|
151
|
+
assert verifier.func.__name__ == "my_verifier"
|
|
152
|
+
|
|
153
|
+
def test_no_function_defined_raises_error(self):
|
|
154
|
+
"""Test that code with no function raises ValueError."""
|
|
155
|
+
code = """
|
|
156
|
+
x = 1
|
|
157
|
+
y = 2
|
|
158
|
+
"""
|
|
159
|
+
with pytest.raises(ValueError, match="No function found in verifier code"):
|
|
160
|
+
sync_verifier_from_string(
|
|
161
|
+
verifier_func=code,
|
|
162
|
+
verifier_id="test-verifier",
|
|
163
|
+
verifier_key="test-key",
|
|
164
|
+
sha256="test-sha",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def test_only_imports_no_function_raises_error(self):
|
|
168
|
+
"""Test that code with only imports and no function raises ValueError."""
|
|
169
|
+
code = """
|
|
170
|
+
from fleet import verifier
|
|
171
|
+
import json
|
|
172
|
+
"""
|
|
173
|
+
with pytest.raises(ValueError, match="No function found in verifier code"):
|
|
174
|
+
sync_verifier_from_string(
|
|
175
|
+
verifier_func=code,
|
|
176
|
+
verifier_id="test-verifier",
|
|
177
|
+
verifier_key="test-key",
|
|
178
|
+
sha256="test-sha",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def test_verifier_with_multiple_functions(self):
|
|
182
|
+
"""Test that first user-defined function is selected when multiple exist."""
|
|
183
|
+
code = """
|
|
184
|
+
def helper_function():
|
|
185
|
+
return "helper"
|
|
186
|
+
|
|
187
|
+
def my_verifier(env):
|
|
188
|
+
return 1.0
|
|
189
|
+
"""
|
|
190
|
+
verifier = sync_verifier_from_string(
|
|
191
|
+
verifier_func=code,
|
|
192
|
+
verifier_id="test-verifier",
|
|
193
|
+
verifier_key="test-key",
|
|
194
|
+
sha256="test-sha",
|
|
195
|
+
)
|
|
196
|
+
assert verifier is not None
|
|
197
|
+
# Should pick the first function (order depends on dict iteration)
|
|
198
|
+
assert verifier.func.__name__ in ["helper_function", "my_verifier"]
|
|
199
|
+
|
|
200
|
+
def test_verifier_with_decorator_usage(self):
|
|
201
|
+
"""Test verifier that would use @verifier decorator in normal usage."""
|
|
202
|
+
code = """
|
|
203
|
+
from fleet.verifiers.verifier import verifier
|
|
204
|
+
|
|
205
|
+
def actual_verifier_function(env, project_key: str = "TEST"):
|
|
206
|
+
# This is the function that should be selected
|
|
207
|
+
return 1.0
|
|
208
|
+
"""
|
|
209
|
+
verifier = sync_verifier_from_string(
|
|
210
|
+
verifier_func=code,
|
|
211
|
+
verifier_id="test-verifier",
|
|
212
|
+
verifier_key="test-key",
|
|
213
|
+
sha256="test-sha",
|
|
214
|
+
)
|
|
215
|
+
assert verifier is not None
|
|
216
|
+
assert verifier.func.__name__ == "actual_verifier_function"
|
|
217
|
+
|
|
218
|
+
def test_verifier_metadata_stored_correctly(self):
|
|
219
|
+
"""Test that verifier metadata is stored correctly."""
|
|
220
|
+
code = """
|
|
221
|
+
def my_verifier(env):
|
|
222
|
+
return 1.0
|
|
223
|
+
"""
|
|
224
|
+
sha256_val = "abcd1234"
|
|
225
|
+
verifier = sync_verifier_from_string(
|
|
226
|
+
verifier_func=code,
|
|
227
|
+
verifier_id="test-verifier-id",
|
|
228
|
+
verifier_key="test-key",
|
|
229
|
+
sha256=sha256_val,
|
|
230
|
+
)
|
|
231
|
+
assert verifier.key == "test-key"
|
|
232
|
+
assert verifier.verifier_id == "test-verifier-id"
|
|
233
|
+
assert verifier._sha256 == sha256_val
|
|
234
|
+
assert verifier._verifier_code == code
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class TestAsyncVerifierFromString:
|
|
238
|
+
"""Tests for async version of verifier_from_string."""
|
|
239
|
+
|
|
240
|
+
def test_basic_async_verifier_without_imports(self):
|
|
241
|
+
"""Test basic async verifier function without any imports."""
|
|
242
|
+
code = """
|
|
243
|
+
async def my_async_verifier(env):
|
|
244
|
+
return 1.0
|
|
245
|
+
"""
|
|
246
|
+
verifier = async_verifier_from_string(
|
|
247
|
+
verifier_func=code,
|
|
248
|
+
verifier_id="test-verifier",
|
|
249
|
+
verifier_key="test-key",
|
|
250
|
+
sha256="test-sha",
|
|
251
|
+
)
|
|
252
|
+
assert verifier is not None
|
|
253
|
+
|
|
254
|
+
def test_async_verifier_with_from_fleet_import_verifier(self):
|
|
255
|
+
"""Test async bug case: 'from fleet import verifier'."""
|
|
256
|
+
code = """
|
|
257
|
+
from fleet import verifier
|
|
258
|
+
|
|
259
|
+
async def my_actual_async_verifier(env):
|
|
260
|
+
return 1.0
|
|
261
|
+
"""
|
|
262
|
+
verifier = async_verifier_from_string(
|
|
263
|
+
verifier_func=code,
|
|
264
|
+
verifier_id="test-verifier",
|
|
265
|
+
verifier_key="test-key",
|
|
266
|
+
sha256="test-sha",
|
|
267
|
+
)
|
|
268
|
+
assert verifier is not None
|
|
269
|
+
assert verifier.func.__name__ == "my_actual_async_verifier"
|
|
270
|
+
|
|
271
|
+
def test_async_verifier_with_multiple_imports(self):
|
|
272
|
+
"""Test async verifier with multiple import statements."""
|
|
273
|
+
code = """
|
|
274
|
+
from fleet import verifier
|
|
275
|
+
from fleet.verifiers.db import IgnoreConfig
|
|
276
|
+
import asyncio
|
|
277
|
+
|
|
278
|
+
async def complex_async_verifier(env):
|
|
279
|
+
await asyncio.sleep(0)
|
|
280
|
+
return 1.0
|
|
281
|
+
"""
|
|
282
|
+
verifier = async_verifier_from_string(
|
|
283
|
+
verifier_func=code,
|
|
284
|
+
verifier_id="test-verifier",
|
|
285
|
+
verifier_key="test-key",
|
|
286
|
+
sha256="test-sha",
|
|
287
|
+
)
|
|
288
|
+
assert verifier is not None
|
|
289
|
+
assert verifier.func.__name__ == "complex_async_verifier"
|
|
290
|
+
|
|
291
|
+
def test_sync_function_in_async_module(self):
|
|
292
|
+
"""Test that sync functions also work in async module."""
|
|
293
|
+
code = """
|
|
294
|
+
from fleet import verifier
|
|
295
|
+
|
|
296
|
+
def sync_verifier_in_async_module(env):
|
|
297
|
+
return 1.0
|
|
298
|
+
"""
|
|
299
|
+
verifier = async_verifier_from_string(
|
|
300
|
+
verifier_func=code,
|
|
301
|
+
verifier_id="test-verifier",
|
|
302
|
+
verifier_key="test-key",
|
|
303
|
+
sha256="test-sha",
|
|
304
|
+
)
|
|
305
|
+
assert verifier is not None
|
|
306
|
+
assert verifier.func.__name__ == "sync_verifier_in_async_module"
|
|
307
|
+
|
|
308
|
+
def test_async_no_function_defined_raises_error(self):
|
|
309
|
+
"""Test that async code with no function raises ValueError."""
|
|
310
|
+
code = """
|
|
311
|
+
x = 1
|
|
312
|
+
y = 2
|
|
313
|
+
"""
|
|
314
|
+
with pytest.raises(ValueError, match="No function found in verifier code"):
|
|
315
|
+
async_verifier_from_string(
|
|
316
|
+
verifier_func=code,
|
|
317
|
+
verifier_id="test-verifier",
|
|
318
|
+
verifier_key="test-key",
|
|
319
|
+
sha256="test-sha",
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
def test_async_only_imports_raises_error(self):
|
|
323
|
+
"""Test that async code with only imports raises ValueError."""
|
|
324
|
+
code = """
|
|
325
|
+
from fleet import verifier
|
|
326
|
+
import asyncio
|
|
327
|
+
"""
|
|
328
|
+
with pytest.raises(ValueError, match="No function found in verifier code"):
|
|
329
|
+
async_verifier_from_string(
|
|
330
|
+
verifier_func=code,
|
|
331
|
+
verifier_id="test-verifier",
|
|
332
|
+
verifier_key="test-key",
|
|
333
|
+
sha256="test-sha",
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
class TestRealWorldScenarios:
|
|
338
|
+
"""Test real-world scenarios from the bug report."""
|
|
339
|
+
|
|
340
|
+
def test_original_bug_report_scenario(self):
|
|
341
|
+
"""Test the exact scenario from the bug report."""
|
|
342
|
+
code = """from fleet import verifier
|
|
343
|
+
def blahblah():
|
|
344
|
+
return 1.0
|
|
345
|
+
"""
|
|
346
|
+
verifier = sync_verifier_from_string(
|
|
347
|
+
verifier_func=code,
|
|
348
|
+
verifier_id="test-verifier",
|
|
349
|
+
verifier_key="test-key",
|
|
350
|
+
sha256="test-sha",
|
|
351
|
+
)
|
|
352
|
+
# Should select 'blahblah', not the imported 'verifier'
|
|
353
|
+
assert verifier.func.__name__ == "blahblah"
|
|
354
|
+
|
|
355
|
+
def test_example_verifier_pattern(self):
|
|
356
|
+
"""Test a pattern similar to example_verifier.py."""
|
|
357
|
+
code = """import fleet
|
|
358
|
+
from fleet.verifiers.verifier import verifier
|
|
359
|
+
from fleet.verifiers.db import IgnoreConfig
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def validate_finish_blue_green_deployment(
|
|
363
|
+
env, final_answer: str = None
|
|
364
|
+
) -> int:
|
|
365
|
+
'''Validate that DEBT-722 and DEBT-720 are marked as Done'''
|
|
366
|
+
return 1.0
|
|
367
|
+
"""
|
|
368
|
+
verifier = sync_verifier_from_string(
|
|
369
|
+
verifier_func=code,
|
|
370
|
+
verifier_id="test-verifier",
|
|
371
|
+
verifier_key="test-key",
|
|
372
|
+
sha256="test-sha",
|
|
373
|
+
)
|
|
374
|
+
assert verifier.func.__name__ == "validate_finish_blue_green_deployment"
|
|
375
|
+
|
|
376
|
+
def test_example_task_pattern_sync(self):
|
|
377
|
+
"""Test a pattern similar to example_task.py sync verifier."""
|
|
378
|
+
code = """from fleet.verifiers.verifier import verifier
|
|
379
|
+
from fleet.verifiers.code import TASK_SUCCESSFUL_SCORE, TASK_FAILED_SCORE
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def create_bug_issue_sync(
|
|
383
|
+
env, project_key: str = "SCRUM", issue_title: str = "Sample Bug"
|
|
384
|
+
) -> float:
|
|
385
|
+
'''Synchronous verifier for remote execution.'''
|
|
386
|
+
try:
|
|
387
|
+
return TASK_SUCCESSFUL_SCORE
|
|
388
|
+
except Exception as e:
|
|
389
|
+
return TASK_FAILED_SCORE
|
|
390
|
+
"""
|
|
391
|
+
verifier = sync_verifier_from_string(
|
|
392
|
+
verifier_func=code,
|
|
393
|
+
verifier_id="test-verifier",
|
|
394
|
+
verifier_key="test-key",
|
|
395
|
+
sha256="test-sha",
|
|
396
|
+
)
|
|
397
|
+
assert verifier.func.__name__ == "create_bug_issue_sync"
|
|
398
|
+
|
|
399
|
+
def test_example_task_pattern_async(self):
|
|
400
|
+
"""Test a pattern similar to example_task.py async verifier."""
|
|
401
|
+
code = """from fleet.verifiers.verifier import verifier
|
|
402
|
+
from fleet.verifiers.code import TASK_SUCCESSFUL_SCORE, TASK_FAILED_SCORE
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
async def create_bug_issue_async(
|
|
406
|
+
env, project_key: str = "SCRUM", issue_title: str = "Sample Bug"
|
|
407
|
+
) -> float:
|
|
408
|
+
'''Async verifier for local execution with async environments.'''
|
|
409
|
+
try:
|
|
410
|
+
return TASK_SUCCESSFUL_SCORE
|
|
411
|
+
except Exception as e:
|
|
412
|
+
return TASK_FAILED_SCORE
|
|
413
|
+
"""
|
|
414
|
+
verifier = async_verifier_from_string(
|
|
415
|
+
verifier_func=code,
|
|
416
|
+
verifier_id="test-verifier",
|
|
417
|
+
verifier_key="test-key",
|
|
418
|
+
sha256="test-sha",
|
|
419
|
+
)
|
|
420
|
+
assert verifier.func.__name__ == "create_bug_issue_async"
|
|
File without changes
|
|
File without changes
|